From c91e127f93f1a60e5625c8bb71c4e09f7858966b Mon Sep 17 00:00:00 2001 From: ZHANG Dapeng Date: Tue, 7 Apr 2020 14:12:53 -0700 Subject: [PATCH 001/104] Start 1.30.0 development cycle --- build.gradle | 2 +- .../src/test/golden/TestDeprecatedService.java.txt | 2 +- compiler/src/test/golden/TestService.java.txt | 2 +- .../src/testLite/golden/TestDeprecatedService.java.txt | 2 +- compiler/src/testLite/golden/TestService.java.txt | 2 +- core/src/main/java/io/grpc/internal/GrpcUtil.java | 2 +- examples/android/clientcache/app/build.gradle | 10 +++++----- examples/android/helloworld/app/build.gradle | 8 ++++---- examples/android/routeguide/app/build.gradle | 8 ++++---- examples/android/strictmode/app/build.gradle | 8 ++++---- examples/build.gradle | 2 +- examples/example-alts/build.gradle | 2 +- examples/example-gauth/build.gradle | 2 +- examples/example-gauth/pom.xml | 4 ++-- examples/example-hostname/build.gradle | 2 +- examples/example-hostname/pom.xml | 4 ++-- examples/example-jwt-auth/build.gradle | 2 +- examples/example-jwt-auth/pom.xml | 4 ++-- .../example-kotlin/android/helloworld/app/build.gradle | 8 ++++---- examples/example-kotlin/build.gradle | 2 +- examples/example-tls/build.gradle | 2 +- examples/example-tls/pom.xml | 4 ++-- examples/example-xds/build.gradle | 2 +- examples/pom.xml | 4 ++-- 24 files changed, 45 insertions(+), 45 deletions(-) diff --git a/build.gradle b/build.gradle index 4c8c919ebdc..f3fd9180769 100644 --- a/build.gradle +++ b/build.gradle @@ -19,7 +19,7 @@ subprojects { apply plugin: "net.ltgt.errorprone" group = "io.grpc" - version = "1.29.0-SNAPSHOT" // CURRENT_GRPC_VERSION + version = "1.30.0-SNAPSHOT" // CURRENT_GRPC_VERSION repositories { maven { // The google mirror is less flaky than mavenCentral() diff --git a/compiler/src/test/golden/TestDeprecatedService.java.txt b/compiler/src/test/golden/TestDeprecatedService.java.txt index cafb4dbe5f8..dd5e771077a 100644 --- a/compiler/src/test/golden/TestDeprecatedService.java.txt +++ b/compiler/src/test/golden/TestDeprecatedService.java.txt @@ -21,7 +21,7 @@ import static io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall; * */ @javax.annotation.Generated( - value = "by gRPC proto compiler (version 1.29.0-SNAPSHOT)", + value = "by gRPC proto compiler (version 1.30.0-SNAPSHOT)", comments = "Source: grpc/testing/compiler/test.proto") @java.lang.Deprecated public final class TestDeprecatedServiceGrpc { diff --git a/compiler/src/test/golden/TestService.java.txt b/compiler/src/test/golden/TestService.java.txt index bf4c3079c5a..55773f669c7 100644 --- a/compiler/src/test/golden/TestService.java.txt +++ b/compiler/src/test/golden/TestService.java.txt @@ -21,7 +21,7 @@ import static io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall; * */ @javax.annotation.Generated( - value = "by gRPC proto compiler (version 1.29.0-SNAPSHOT)", + value = "by gRPC proto compiler (version 1.30.0-SNAPSHOT)", comments = "Source: grpc/testing/compiler/test.proto") public final class TestServiceGrpc { diff --git a/compiler/src/testLite/golden/TestDeprecatedService.java.txt b/compiler/src/testLite/golden/TestDeprecatedService.java.txt index e2eb97adacb..9e25eebfdb0 100644 --- a/compiler/src/testLite/golden/TestDeprecatedService.java.txt +++ b/compiler/src/testLite/golden/TestDeprecatedService.java.txt @@ -21,7 +21,7 @@ import static io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall; * */ @javax.annotation.Generated( - value = "by gRPC proto compiler (version 1.29.0-SNAPSHOT)", + value = "by gRPC proto compiler (version 1.30.0-SNAPSHOT)", comments = "Source: grpc/testing/compiler/test.proto") @java.lang.Deprecated public final class TestDeprecatedServiceGrpc { diff --git a/compiler/src/testLite/golden/TestService.java.txt b/compiler/src/testLite/golden/TestService.java.txt index 1d0e4305469..07c58379005 100644 --- a/compiler/src/testLite/golden/TestService.java.txt +++ b/compiler/src/testLite/golden/TestService.java.txt @@ -21,7 +21,7 @@ import static io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall; * */ @javax.annotation.Generated( - value = "by gRPC proto compiler (version 1.29.0-SNAPSHOT)", + value = "by gRPC proto compiler (version 1.30.0-SNAPSHOT)", comments = "Source: grpc/testing/compiler/test.proto") public final class TestServiceGrpc { diff --git a/core/src/main/java/io/grpc/internal/GrpcUtil.java b/core/src/main/java/io/grpc/internal/GrpcUtil.java index 517cc267d1b..f6cac535c0e 100644 --- a/core/src/main/java/io/grpc/internal/GrpcUtil.java +++ b/core/src/main/java/io/grpc/internal/GrpcUtil.java @@ -196,7 +196,7 @@ public byte[] parseAsciiString(byte[] serialized) { public static final Splitter ACCEPT_ENCODING_SPLITTER = Splitter.on(',').trimResults(); - private static final String IMPLEMENTATION_VERSION = "1.29.0-SNAPSHOT"; // CURRENT_GRPC_VERSION + private static final String IMPLEMENTATION_VERSION = "1.30.0-SNAPSHOT"; // CURRENT_GRPC_VERSION /** * The default timeout in nanos for a keepalive ping request. diff --git a/examples/android/clientcache/app/build.gradle b/examples/android/clientcache/app/build.gradle index 477cbaa66b9..b03ec45f86a 100644 --- a/examples/android/clientcache/app/build.gradle +++ b/examples/android/clientcache/app/build.gradle @@ -30,7 +30,7 @@ android { protobuf { protoc { artifact = 'com.google.protobuf:protoc:3.11.0' } plugins { - grpc { artifact = 'io.grpc:protoc-gen-grpc-java:1.29.0-SNAPSHOT' // CURRENT_GRPC_VERSION + grpc { artifact = 'io.grpc:protoc-gen-grpc-java:1.30.0-SNAPSHOT' // CURRENT_GRPC_VERSION } } generateProtoTasks { @@ -50,12 +50,12 @@ dependencies { implementation 'com.android.support:appcompat-v7:27.0.2' // You need to build grpc-java to obtain these libraries below. - implementation 'io.grpc:grpc-okhttp:1.29.0-SNAPSHOT' // CURRENT_GRPC_VERSION - implementation 'io.grpc:grpc-protobuf-lite:1.29.0-SNAPSHOT' // CURRENT_GRPC_VERSION - implementation 'io.grpc:grpc-stub:1.29.0-SNAPSHOT' // CURRENT_GRPC_VERSION + implementation 'io.grpc:grpc-okhttp:1.30.0-SNAPSHOT' // CURRENT_GRPC_VERSION + implementation 'io.grpc:grpc-protobuf-lite:1.30.0-SNAPSHOT' // CURRENT_GRPC_VERSION + implementation 'io.grpc:grpc-stub:1.30.0-SNAPSHOT' // CURRENT_GRPC_VERSION implementation 'javax.annotation:javax.annotation-api:1.2' testImplementation 'junit:junit:4.12' testImplementation 'com.google.truth:truth:1.0.1' - testImplementation 'io.grpc:grpc-testing:1.29.0-SNAPSHOT' // CURRENT_GRPC_VERSION + testImplementation 'io.grpc:grpc-testing:1.30.0-SNAPSHOT' // CURRENT_GRPC_VERSION } diff --git a/examples/android/helloworld/app/build.gradle b/examples/android/helloworld/app/build.gradle index e5a00edcf06..43a25633f8a 100644 --- a/examples/android/helloworld/app/build.gradle +++ b/examples/android/helloworld/app/build.gradle @@ -29,7 +29,7 @@ android { protobuf { protoc { artifact = 'com.google.protobuf:protoc:3.11.0' } plugins { - grpc { artifact = 'io.grpc:protoc-gen-grpc-java:1.29.0-SNAPSHOT' // CURRENT_GRPC_VERSION + grpc { artifact = 'io.grpc:protoc-gen-grpc-java:1.30.0-SNAPSHOT' // CURRENT_GRPC_VERSION } } generateProtoTasks { @@ -49,8 +49,8 @@ dependencies { implementation 'com.android.support:appcompat-v7:27.0.2' // You need to build grpc-java to obtain these libraries below. - implementation 'io.grpc:grpc-okhttp:1.29.0-SNAPSHOT' // CURRENT_GRPC_VERSION - implementation 'io.grpc:grpc-protobuf-lite:1.29.0-SNAPSHOT' // CURRENT_GRPC_VERSION - implementation 'io.grpc:grpc-stub:1.29.0-SNAPSHOT' // CURRENT_GRPC_VERSION + implementation 'io.grpc:grpc-okhttp:1.30.0-SNAPSHOT' // CURRENT_GRPC_VERSION + implementation 'io.grpc:grpc-protobuf-lite:1.30.0-SNAPSHOT' // CURRENT_GRPC_VERSION + implementation 'io.grpc:grpc-stub:1.30.0-SNAPSHOT' // CURRENT_GRPC_VERSION implementation 'javax.annotation:javax.annotation-api:1.2' } diff --git a/examples/android/routeguide/app/build.gradle b/examples/android/routeguide/app/build.gradle index bdce8397c5c..698557f09bf 100644 --- a/examples/android/routeguide/app/build.gradle +++ b/examples/android/routeguide/app/build.gradle @@ -28,7 +28,7 @@ android { protobuf { protoc { artifact = 'com.google.protobuf:protoc:3.11.0' } plugins { - grpc { artifact = 'io.grpc:protoc-gen-grpc-java:1.29.0-SNAPSHOT' // CURRENT_GRPC_VERSION + grpc { artifact = 'io.grpc:protoc-gen-grpc-java:1.30.0-SNAPSHOT' // CURRENT_GRPC_VERSION } } generateProtoTasks { @@ -48,8 +48,8 @@ dependencies { implementation 'com.android.support:appcompat-v7:27.0.2' // You need to build grpc-java to obtain these libraries below. - implementation 'io.grpc:grpc-okhttp:1.29.0-SNAPSHOT' // CURRENT_GRPC_VERSION - implementation 'io.grpc:grpc-protobuf-lite:1.29.0-SNAPSHOT' // CURRENT_GRPC_VERSION - implementation 'io.grpc:grpc-stub:1.29.0-SNAPSHOT' // CURRENT_GRPC_VERSION + implementation 'io.grpc:grpc-okhttp:1.30.0-SNAPSHOT' // CURRENT_GRPC_VERSION + implementation 'io.grpc:grpc-protobuf-lite:1.30.0-SNAPSHOT' // CURRENT_GRPC_VERSION + implementation 'io.grpc:grpc-stub:1.30.0-SNAPSHOT' // CURRENT_GRPC_VERSION implementation 'javax.annotation:javax.annotation-api:1.2' } diff --git a/examples/android/strictmode/app/build.gradle b/examples/android/strictmode/app/build.gradle index 643e25afdfc..fd8284aca80 100644 --- a/examples/android/strictmode/app/build.gradle +++ b/examples/android/strictmode/app/build.gradle @@ -29,7 +29,7 @@ android { protobuf { protoc { artifact = 'com.google.protobuf:protoc:3.11.0' } plugins { - grpc { artifact = 'io.grpc:protoc-gen-grpc-java:1.29.0-SNAPSHOT' // CURRENT_GRPC_VERSION + grpc { artifact = 'io.grpc:protoc-gen-grpc-java:1.30.0-SNAPSHOT' // CURRENT_GRPC_VERSION } } generateProtoTasks { @@ -49,8 +49,8 @@ dependencies { implementation 'com.android.support:appcompat-v7:28.0.0' // You need to build grpc-java to obtain these libraries below. - implementation 'io.grpc:grpc-okhttp:1.29.0-SNAPSHOT' // CURRENT_GRPC_VERSION - implementation 'io.grpc:grpc-protobuf-lite:1.29.0-SNAPSHOT' // CURRENT_GRPC_VERSION - implementation 'io.grpc:grpc-stub:1.29.0-SNAPSHOT' // CURRENT_GRPC_VERSION + implementation 'io.grpc:grpc-okhttp:1.30.0-SNAPSHOT' // CURRENT_GRPC_VERSION + implementation 'io.grpc:grpc-protobuf-lite:1.30.0-SNAPSHOT' // CURRENT_GRPC_VERSION + implementation 'io.grpc:grpc-stub:1.30.0-SNAPSHOT' // CURRENT_GRPC_VERSION implementation 'javax.annotation:javax.annotation-api:1.2' } diff --git a/examples/build.gradle b/examples/build.gradle index 435ab7ec583..919001774cc 100644 --- a/examples/build.gradle +++ b/examples/build.gradle @@ -22,7 +22,7 @@ targetCompatibility = 1.7 // Feel free to delete the comment at the next line. It is just for safely // updating the version in our release process. -def grpcVersion = '1.29.0-SNAPSHOT' // CURRENT_GRPC_VERSION +def grpcVersion = '1.30.0-SNAPSHOT' // CURRENT_GRPC_VERSION def protobufVersion = '3.11.0' def protocVersion = protobufVersion diff --git a/examples/example-alts/build.gradle b/examples/example-alts/build.gradle index 361a44ff150..717d8c1cf46 100644 --- a/examples/example-alts/build.gradle +++ b/examples/example-alts/build.gradle @@ -23,7 +23,7 @@ targetCompatibility = 1.7 // Feel free to delete the comment at the next line. It is just for safely // updating the version in our release process. -def grpcVersion = '1.29.0-SNAPSHOT' // CURRENT_GRPC_VERSION +def grpcVersion = '1.30.0-SNAPSHOT' // CURRENT_GRPC_VERSION def protocVersion = '3.11.0' dependencies { diff --git a/examples/example-gauth/build.gradle b/examples/example-gauth/build.gradle index ec74b652bde..01d01524c2c 100644 --- a/examples/example-gauth/build.gradle +++ b/examples/example-gauth/build.gradle @@ -23,7 +23,7 @@ targetCompatibility = 1.7 // Feel free to delete the comment at the next line. It is just for safely // updating the version in our release process. -def grpcVersion = '1.29.0-SNAPSHOT' // CURRENT_GRPC_VERSION +def grpcVersion = '1.30.0-SNAPSHOT' // CURRENT_GRPC_VERSION def protobufVersion = '3.11.0' def protocVersion = protobufVersion diff --git a/examples/example-gauth/pom.xml b/examples/example-gauth/pom.xml index b1f2ef2e1df..e1916c0a1f4 100644 --- a/examples/example-gauth/pom.xml +++ b/examples/example-gauth/pom.xml @@ -6,13 +6,13 @@ jar - 1.29.0-SNAPSHOT + 1.30.0-SNAPSHOT example-gauth https://github.com/grpc/grpc-java UTF-8 - 1.29.0-SNAPSHOT + 1.30.0-SNAPSHOT 3.11.0 1.7 diff --git a/examples/example-hostname/build.gradle b/examples/example-hostname/build.gradle index 1a20ee19260..a55d7fcaedf 100644 --- a/examples/example-hostname/build.gradle +++ b/examples/example-hostname/build.gradle @@ -20,7 +20,7 @@ targetCompatibility = 1.7 // Feel free to delete the comment at the next line. It is just for safely // updating the version in our release process. -def grpcVersion = '1.29.0-SNAPSHOT' // CURRENT_GRPC_VERSION +def grpcVersion = '1.30.0-SNAPSHOT' // CURRENT_GRPC_VERSION def protobufVersion = '3.11.0' dependencies { diff --git a/examples/example-hostname/pom.xml b/examples/example-hostname/pom.xml index 1aa64e88938..80557102f40 100644 --- a/examples/example-hostname/pom.xml +++ b/examples/example-hostname/pom.xml @@ -6,13 +6,13 @@ jar - 1.29.0-SNAPSHOT + 1.30.0-SNAPSHOT example-hostname https://github.com/grpc/grpc-java UTF-8 - 1.29.0-SNAPSHOT + 1.30.0-SNAPSHOT 3.11.0 1.7 diff --git a/examples/example-jwt-auth/build.gradle b/examples/example-jwt-auth/build.gradle index 8b2b5760e86..e19dd9e94db 100644 --- a/examples/example-jwt-auth/build.gradle +++ b/examples/example-jwt-auth/build.gradle @@ -22,7 +22,7 @@ targetCompatibility = 1.7 // Feel free to delete the comment at the next line. It is just for safely // updating the version in our release process. -def grpcVersion = '1.29.0-SNAPSHOT' // CURRENT_GRPC_VERSION +def grpcVersion = '1.30.0-SNAPSHOT' // CURRENT_GRPC_VERSION def protobufVersion = '3.11.0' def protocVersion = protobufVersion diff --git a/examples/example-jwt-auth/pom.xml b/examples/example-jwt-auth/pom.xml index a40481b5087..07c89cc5b20 100644 --- a/examples/example-jwt-auth/pom.xml +++ b/examples/example-jwt-auth/pom.xml @@ -7,13 +7,13 @@ jar - 1.29.0-SNAPSHOT + 1.30.0-SNAPSHOT example-jwt-auth https://github.com/grpc/grpc-java UTF-8 - 1.29.0-SNAPSHOT + 1.30.0-SNAPSHOT 3.11.0 3.11.0 diff --git a/examples/example-kotlin/android/helloworld/app/build.gradle b/examples/example-kotlin/android/helloworld/app/build.gradle index 9f32d958623..9558f1a06fe 100644 --- a/examples/example-kotlin/android/helloworld/app/build.gradle +++ b/examples/example-kotlin/android/helloworld/app/build.gradle @@ -51,7 +51,7 @@ android { protobuf { protoc { artifact = 'com.google.protobuf:protoc:3.11.0' } plugins { - grpc { artifact = 'io.grpc:protoc-gen-grpc-java:1.29.0-SNAPSHOT' // CURRENT_GRPC_VERSION + grpc { artifact = 'io.grpc:protoc-gen-grpc-java:1.30.0-SNAPSHOT' // CURRENT_GRPC_VERSION } } generateProtoTasks { @@ -73,9 +73,9 @@ dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" // You need to build grpc-java to obtain these libraries below. - implementation 'io.grpc:grpc-okhttp:1.29.0-SNAPSHOT' // CURRENT_GRPC_VERSION - implementation 'io.grpc:grpc-protobuf-lite:1.29.0-SNAPSHOT' // CURRENT_GRPC_VERSION - implementation 'io.grpc:grpc-stub:1.29.0-SNAPSHOT' // CURRENT_GRPC_VERSION + implementation 'io.grpc:grpc-okhttp:1.30.0-SNAPSHOT' // CURRENT_GRPC_VERSION + implementation 'io.grpc:grpc-protobuf-lite:1.30.0-SNAPSHOT' // CURRENT_GRPC_VERSION + implementation 'io.grpc:grpc-stub:1.30.0-SNAPSHOT' // CURRENT_GRPC_VERSION } repositories { mavenCentral() } diff --git a/examples/example-kotlin/build.gradle b/examples/example-kotlin/build.gradle index e3e538335b4..41b79e59566 100644 --- a/examples/example-kotlin/build.gradle +++ b/examples/example-kotlin/build.gradle @@ -25,7 +25,7 @@ repositories { // Feel free to delete the comment at the next line. It is just for safely // updating the version in our release process. -def grpcVersion = '1.29.0-SNAPSHOT' // CURRENT_GRPC_VERSION +def grpcVersion = '1.30.0-SNAPSHOT' // CURRENT_GRPC_VERSION dependencies { def kotlinVersion = plugins.findPlugin("org.jetbrains.kotlin.jvm").kotlinPluginVersion diff --git a/examples/example-tls/build.gradle b/examples/example-tls/build.gradle index 39db5253126..d023ae46722 100644 --- a/examples/example-tls/build.gradle +++ b/examples/example-tls/build.gradle @@ -23,7 +23,7 @@ targetCompatibility = 1.7 // Feel free to delete the comment at the next line. It is just for safely // updating the version in our release process. -def grpcVersion = '1.29.0-SNAPSHOT' // CURRENT_GRPC_VERSION +def grpcVersion = '1.30.0-SNAPSHOT' // CURRENT_GRPC_VERSION def nettyTcNativeVersion = '2.0.30.Final' def protocVersion = '3.11.0' diff --git a/examples/example-tls/pom.xml b/examples/example-tls/pom.xml index 4ea76cb3d49..c535c257887 100644 --- a/examples/example-tls/pom.xml +++ b/examples/example-tls/pom.xml @@ -6,13 +6,13 @@ jar - 1.29.0-SNAPSHOT + 1.30.0-SNAPSHOT example-tls https://github.com/grpc/grpc-java UTF-8 - 1.29.0-SNAPSHOT + 1.30.0-SNAPSHOT 3.11.0 2.0.30.Final diff --git a/examples/example-xds/build.gradle b/examples/example-xds/build.gradle index 97cd7d767fc..b8ed5d6c02c 100644 --- a/examples/example-xds/build.gradle +++ b/examples/example-xds/build.gradle @@ -18,7 +18,7 @@ targetCompatibility = 1.7 // Feel free to delete the comment at the next line. It is just for safely // updating the version in our release process. -def grpcVersion = '1.29.0-SNAPSHOT' // CURRENT_GRPC_VERSION +def grpcVersion = '1.30.0-SNAPSHOT' // CURRENT_GRPC_VERSION dependencies { // This example's client is the same as the helloworld client. We depend on the helloworld diff --git a/examples/pom.xml b/examples/pom.xml index 379fca7b8b8..f12ef963519 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -6,13 +6,13 @@ jar - 1.29.0-SNAPSHOT + 1.30.0-SNAPSHOT examples https://github.com/grpc/grpc-java UTF-8 - 1.29.0-SNAPSHOT + 1.30.0-SNAPSHOT 3.11.0 3.11.0 From 829a7c5a29621068a7d6041623ad659f428a2ea3 Mon Sep 17 00:00:00 2001 From: sanjaypujare Date: Tue, 7 Apr 2020 15:00:35 -0700 Subject: [PATCH 002/104] xds: remove hardcoded port and unneeded test (#6908) --- .../io/grpc/xds/XdsServerBuilderTest.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/xds/src/test/java/io/grpc/xds/XdsServerBuilderTest.java b/xds/src/test/java/io/grpc/xds/XdsServerBuilderTest.java index 5f80baa6dc1..5562347a978 100644 --- a/xds/src/test/java/io/grpc/xds/XdsServerBuilderTest.java +++ b/xds/src/test/java/io/grpc/xds/XdsServerBuilderTest.java @@ -16,7 +16,6 @@ package io.grpc.xds; -import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -25,6 +24,7 @@ import io.grpc.xds.internal.sds.SdsProtocolNegotiators.ServerSdsProtocolNegotiator; import io.grpc.xds.internal.sds.XdsServerBuilder; import java.io.IOException; +import java.net.ServerSocket; import java.util.concurrent.TimeUnit; import org.junit.Test; import org.junit.runner.RunWith; @@ -36,20 +36,13 @@ @RunWith(JUnit4.class) public class XdsServerBuilderTest { - @Test - public void buildsXdsServerBuilder() { - XdsServerBuilder builder = XdsServerBuilder.forPort(8080); - assertThat(builder).isInstanceOf(XdsServerBuilder.class); - Server server = builder.build(); - assertThat(server).isNotNull(); - } - @Test public void xdsServer_callsShutdown() throws IOException, InterruptedException { - XdsServerBuilder builder = XdsServerBuilder.forPort(8080); + int port = findFreePort(); + XdsServerBuilder builder = XdsServerBuilder.forPort(port); XdsClient mockXdsClient = mock(XdsClient.class); XdsClientWrapperForServerSds xdsClientWrapperForServerSds = - new XdsClientWrapperForServerSds(8080, mockXdsClient, null); + new XdsClientWrapperForServerSds(port, mockXdsClient, null); ServerSdsProtocolNegotiator serverSdsProtocolNegotiator = new ServerSdsProtocolNegotiator(null, xdsClientWrapperForServerSds); Server xdsServer = builder.buildServer(serverSdsProtocolNegotiator); @@ -58,4 +51,11 @@ public void xdsServer_callsShutdown() throws IOException, InterruptedException { xdsServer.awaitTermination(500L, TimeUnit.MILLISECONDS); verify(mockXdsClient, times(1)).shutdown(); } + + private static int findFreePort() throws IOException { + try (ServerSocket socket = new ServerSocket(0)) { + socket.setReuseAddress(true); + return socket.getLocalPort(); + } + } } From 54f9e29cdb205062158e19d9dc1a567afd61a318 Mon Sep 17 00:00:00 2001 From: Chengyuan Zhang Date: Tue, 7 Apr 2020 22:31:36 +0000 Subject: [PATCH 003/104] xds: fix lint warnings (#6907) --- xds/src/main/java/io/grpc/xds/XdsNameResolver.java | 7 +++---- .../io/grpc/xds/internal/sds/SdsProtocolNegotiators.java | 4 ++++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/xds/src/main/java/io/grpc/xds/XdsNameResolver.java b/xds/src/main/java/io/grpc/xds/XdsNameResolver.java index ac59f418987..7f9d2a6cd9b 100644 --- a/xds/src/main/java/io/grpc/xds/XdsNameResolver.java +++ b/xds/src/main/java/io/grpc/xds/XdsNameResolver.java @@ -148,7 +148,6 @@ private class ConfigWatcherImpl implements ConfigWatcher { this.listener = listener; } - @SuppressWarnings("unchecked") @Override public void onConfigChanged(ConfigUpdate update) { Map rawLbConfig; @@ -220,7 +219,7 @@ public void onError(Status error) { } } - private static Map generateXdsRoutingRawConfig(List routesUpdate) { + private static ImmutableMap generateXdsRoutingRawConfig(List routesUpdate) { List routes = new ArrayList<>(routesUpdate.size()); Map actions = new LinkedHashMap<>(); Map exitingActions = new HashMap<>(); @@ -275,7 +274,7 @@ public void onError(Status error) { ImmutableMap.of("route", routes, "action", actions)); } - private static Map generateWeightedTargetRawConfig( + private static ImmutableMap generateWeightedTargetRawConfig( List clusterWeights) { Map targets = new LinkedHashMap<>(); for (ClusterWeight clusterWeight : clusterWeights) { @@ -292,7 +291,7 @@ public void onError(Status error) { ImmutableMap.of("targets", targets)); } - private static Map generateCdsRawConfig(String clusterName) { + private static ImmutableMap generateCdsRawConfig(String clusterName) { return ImmutableMap.of(XdsLbPolicies.CDS_POLICY_NAME, ImmutableMap.of("cluster", clusterName)); } diff --git a/xds/src/main/java/io/grpc/xds/internal/sds/SdsProtocolNegotiators.java b/xds/src/main/java/io/grpc/xds/internal/sds/SdsProtocolNegotiators.java index c6fc2d94072..8270d2dad11 100644 --- a/xds/src/main/java/io/grpc/xds/internal/sds/SdsProtocolNegotiators.java +++ b/xds/src/main/java/io/grpc/xds/internal/sds/SdsProtocolNegotiators.java @@ -55,6 +55,10 @@ @VisibleForTesting public final class SdsProtocolNegotiators { + // Prevent instantiation. + private SdsProtocolNegotiators() { + } + private static final Logger logger = Logger.getLogger(SdsProtocolNegotiators.class.getName()); private static final AsciiString SCHEME = AsciiString.of("https"); From 2119fac35b7ceeef97faed5caaee29c3385bf54c Mon Sep 17 00:00:00 2001 From: Andrew Gasparovic <55992101+agasparovic-sabre@users.noreply.github.com> Date: Wed, 8 Apr 2020 14:12:00 -0700 Subject: [PATCH 004/104] compiler: Remove unused function (#6911) --- compiler/src/java_plugin/cpp/java_generator.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/compiler/src/java_plugin/cpp/java_generator.cpp b/compiler/src/java_plugin/cpp/java_generator.cpp index db914ce12e1..695ef8581ba 100644 --- a/compiler/src/java_plugin/cpp/java_generator.cpp +++ b/compiler/src/java_plugin/cpp/java_generator.cpp @@ -161,10 +161,6 @@ static inline std::string MethodIdFieldName(const MethodDescriptor* method) { return "METHODID_" + ToAllUpperCase(method->name()); } -static inline bool ShouldGenerateAsLite(const Descriptor* desc) { - return false; -} - static inline std::string MessageFullJavaName(const Descriptor* desc) { return google::protobuf::compiler::java::ClassName(desc); } From ec010c1a62e96515653dbfb2273b3f07bf0cced2 Mon Sep 17 00:00:00 2001 From: Eric Anderson Date: Thu, 9 Apr 2020 14:01:36 -0700 Subject: [PATCH 005/104] core: Delay transport shutdown during updateAddresses() A user has been seeing "InternalSubchannel closed transport due to address change" errors (b/153064566). It is unclear if they are predomenent, but they are at least adding noise. Since #2562 is still far from being generally solved, we delay the shutdown a while to side-step the race. --- .../io/grpc/internal/InternalSubchannel.java | 44 ++++++++++++-- .../io/grpc/internal/ManagedChannelImpl.java | 1 - .../grpc/internal/InternalSubchannelTest.java | 57 +++++++++++++++++++ .../ManagedChannelImplIdlenessTest.java | 6 ++ 4 files changed, 103 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/io/grpc/internal/InternalSubchannel.java b/core/src/main/java/io/grpc/internal/InternalSubchannel.java index d84c7710384..97be3f26e0e 100644 --- a/core/src/main/java/io/grpc/internal/InternalSubchannel.java +++ b/core/src/main/java/io/grpc/internal/InternalSubchannel.java @@ -111,6 +111,10 @@ final class InternalSubchannel implements InternalInstrumented, Tr @Nullable private ScheduledHandle reconnectTask; + @Nullable + private ScheduledHandle shutdownDueToUpdateTask; + @Nullable + private ManagedClientTransport shutdownDueToUpdateTransport; /** * All transports that are not terminated. At the very least the value of {@link #activeTransport} @@ -354,7 +358,9 @@ public void run() { addressIndex.reset(); gotoNonErrorState(IDLE); } else { - savedTransport = pendingTransport; + pendingTransport.shutdown( + Status.UNAVAILABLE.withDescription( + "InternalSubchannel closed pending transport due to address change")); pendingTransport = null; addressIndex.reset(); startNewTransport(); @@ -362,9 +368,33 @@ public void run() { } } if (savedTransport != null) { - savedTransport.shutdown( - Status.UNAVAILABLE.withDescription( - "InternalSubchannel closed transport due to address change")); + if (shutdownDueToUpdateTask != null) { + // Keeping track of multiple shutdown tasks adds complexity, and shouldn't generally be + // necessary. This transport has probably already had plenty of time. + shutdownDueToUpdateTransport.shutdown( + Status.UNAVAILABLE.withDescription( + "InternalSubchannel closed transport early due to address change")); + shutdownDueToUpdateTask.cancel(); + shutdownDueToUpdateTask = null; + shutdownDueToUpdateTransport = null; + } + // Avoid needless RPC failures by delaying the shutdown. See + // https://github.com/grpc/grpc-java/issues/2562 + shutdownDueToUpdateTransport = savedTransport; + shutdownDueToUpdateTask = syncContext.schedule( + new Runnable() { + @Override public void run() { + ManagedClientTransport transport = shutdownDueToUpdateTransport; + shutdownDueToUpdateTask = null; + shutdownDueToUpdateTransport = null; + transport.shutdown( + Status.UNAVAILABLE.withDescription( + "InternalSubchannel closed transport due to address change")); + } + }, + ManagedChannelImpl.SUBCHANNEL_SHUTDOWN_DELAY_SECONDS, + TimeUnit.SECONDS, + scheduledExecutor); } } }); @@ -390,6 +420,12 @@ public void run() { handleTermination(); } // else: the callback will be run once all transports have been terminated cancelReconnectTask(); + if (shutdownDueToUpdateTask != null) { + shutdownDueToUpdateTask.cancel(); + shutdownDueToUpdateTransport.shutdown(reason); + shutdownDueToUpdateTask = null; + shutdownDueToUpdateTransport = null; + } if (savedActiveTransport != null) { savedActiveTransport.shutdown(reason); } diff --git a/core/src/main/java/io/grpc/internal/ManagedChannelImpl.java b/core/src/main/java/io/grpc/internal/ManagedChannelImpl.java index 94473a9bf5a..55f73db81d4 100644 --- a/core/src/main/java/io/grpc/internal/ManagedChannelImpl.java +++ b/core/src/main/java/io/grpc/internal/ManagedChannelImpl.java @@ -114,7 +114,6 @@ final class ManagedChannelImpl extends ManagedChannel implements static final long IDLE_TIMEOUT_MILLIS_DISABLE = -1; - @VisibleForTesting static final long SUBCHANNEL_SHUTDOWN_DELAY_SECONDS = 5; @VisibleForTesting diff --git a/core/src/test/java/io/grpc/internal/InternalSubchannelTest.java b/core/src/test/java/io/grpc/internal/InternalSubchannelTest.java index 985be0f68a9..37099995835 100644 --- a/core/src/test/java/io/grpc/internal/InternalSubchannelTest.java +++ b/core/src/test/java/io/grpc/internal/InternalSubchannelTest.java @@ -57,6 +57,7 @@ import java.util.LinkedList; import java.util.List; import java.util.concurrent.BlockingQueue; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import org.junit.After; import org.junit.Before; @@ -700,6 +701,9 @@ public void updateAddresses_eagListWithNull_throws() { Arrays.asList(new EquivalentAddressGroup(Arrays.asList(addr3, addr4)))); assertExactCallbackInvokes("onStateChange:IDLE"); assertEquals(IDLE, internalSubchannel.getState()); + verify(transports.peek().transport, never()).shutdown(any(Status.class)); + fakeClock.forwardNanos( + TimeUnit.SECONDS.toNanos(ManagedChannelImpl.SUBCHANNEL_SHUTDOWN_DELAY_SECONDS)); verify(transports.peek().transport).shutdown(any(Status.class)); // And new addresses chosen when re-connecting @@ -784,6 +788,59 @@ public void updateAddresses_eagListWithNull_throws() { fakeClock.forwardNanos(10); // Drain retry, but don't care about result } + @Test public void updateAddresses_disjoint_readyTwice() { + SocketAddress addr1 = mock(SocketAddress.class); + createInternalSubchannel(addr1); + assertEquals(IDLE, internalSubchannel.getState()); + + // Address connects + assertNull(internalSubchannel.obtainActiveTransport()); + assertExactCallbackInvokes("onStateChange:CONNECTING"); + verify(mockTransportFactory) + .newClientTransport( + eq(addr1), + eq(createClientTransportOptions()), + isA(TransportLogger.class)); + transports.peek().listener.transportReady(); + assertExactCallbackInvokes("onStateChange:READY"); + assertEquals(READY, internalSubchannel.getState()); + + // Update addresses + SocketAddress addr2 = mock(SocketAddress.class); + internalSubchannel.updateAddresses( + Arrays.asList(new EquivalentAddressGroup(Arrays.asList(addr2)))); + assertExactCallbackInvokes("onStateChange:IDLE"); + assertEquals(IDLE, internalSubchannel.getState()); + ConnectionClientTransport firstTransport = transports.poll().transport; + verify(firstTransport, never()).shutdown(any(Status.class)); + + // Address connects + assertNull(internalSubchannel.obtainActiveTransport()); + assertExactCallbackInvokes("onStateChange:CONNECTING"); + verify(mockTransportFactory) + .newClientTransport( + eq(addr2), + eq(createClientTransportOptions()), + isA(TransportLogger.class)); + transports.peek().listener.transportReady(); + assertExactCallbackInvokes("onStateChange:READY"); + assertEquals(READY, internalSubchannel.getState()); + + // Update addresses + SocketAddress addr3 = mock(SocketAddress.class); + internalSubchannel.updateAddresses( + Arrays.asList(new EquivalentAddressGroup(Arrays.asList(addr3)))); + assertExactCallbackInvokes("onStateChange:IDLE"); + assertEquals(IDLE, internalSubchannel.getState()); + // Earlier transport is shutdown eagerly + verify(firstTransport).shutdown(any(Status.class)); + ConnectionClientTransport secondTransport = transports.peek().transport; + verify(secondTransport, never()).shutdown(any(Status.class)); + + internalSubchannel.shutdown(SHUTDOWN_REASON); + verify(secondTransport).shutdown(any(Status.class)); + } + @Test public void connectIsLazy() { SocketAddress addr = mock(SocketAddress.class); diff --git a/core/src/test/java/io/grpc/internal/ManagedChannelImplIdlenessTest.java b/core/src/test/java/io/grpc/internal/ManagedChannelImplIdlenessTest.java index 22cd9879e85..4439383c0e0 100644 --- a/core/src/test/java/io/grpc/internal/ManagedChannelImplIdlenessTest.java +++ b/core/src/test/java/io/grpc/internal/ManagedChannelImplIdlenessTest.java @@ -367,6 +367,9 @@ public void updateSubchannelAddresses_newAddressConnects() { requestConnectionSafely(helper, subchannel); MockClientTransportInfo t1 = newTransports.poll(); t1.listener.transportReady(); + + // Drain InternalSubchannel's delayed shutdown on updateAddresses + timer.forwardTime(ManagedChannelImpl.SUBCHANNEL_SHUTDOWN_DELAY_SECONDS, TimeUnit.SECONDS); } @Test @@ -459,6 +462,9 @@ public void updateOobChannelAddresses_newAddressConnects() { oobChannel.newCall(method, CallOptions.DEFAULT).start(mockCallListener, new Metadata()); MockClientTransportInfo t1 = newTransports.poll(); t1.listener.transportReady(); + + // Drain InternalSubchannel's delayed shutdown on updateAddresses + timer.forwardTime(ManagedChannelImpl.SUBCHANNEL_SHUTDOWN_DELAY_SECONDS, TimeUnit.SECONDS); } @Test From 833a3ff2933d4c9ef0f4ab6c54695165582606c3 Mon Sep 17 00:00:00 2001 From: Chengyuan Zhang Date: Fri, 10 Apr 2020 01:42:58 +0000 Subject: [PATCH 006/104] xds: handle EDS update with no localities (#6915) Allow receiving empty LocalityLbEnpoints in EDS responses, it is LB policies' responsibility to go to TRANSIENT_FAILURE immediately when receiving an update with no locality. --- .../main/java/io/grpc/xds/LocalityStore.java | 7 ++++++- .../main/java/io/grpc/xds/XdsClientImpl.java | 4 ---- .../java/io/grpc/xds/LocalityStoreTest.java | 11 ++++++++++ .../java/io/grpc/xds/XdsClientImplTest.java | 21 +++++++++++++++++++ 4 files changed, 38 insertions(+), 5 deletions(-) diff --git a/xds/src/main/java/io/grpc/xds/LocalityStore.java b/xds/src/main/java/io/grpc/xds/LocalityStore.java index ca54757ce70..41148e0323e 100644 --- a/xds/src/main/java/io/grpc/xds/LocalityStore.java +++ b/xds/src/main/java/io/grpc/xds/LocalityStore.java @@ -473,7 +473,12 @@ void updateLocalities(Map localityInfoMap) { "Priority {0} contains localities: {1}", i, priorityTable.get(i)); } } - + if (priorityTable.isEmpty()) { + helper.updateBalancingState( + TRANSIENT_FAILURE, + new ErrorPicker(Status.UNAVAILABLE.withDescription("Received 0 locality"))); + return; + } currentPriority = -1; failOver(); } diff --git a/xds/src/main/java/io/grpc/xds/XdsClientImpl.java b/xds/src/main/java/io/grpc/xds/XdsClientImpl.java index e963f3bdd76..5498a0c81b7 100644 --- a/xds/src/main/java/io/grpc/xds/XdsClientImpl.java +++ b/xds/src/main/java/io/grpc/xds/XdsClientImpl.java @@ -1189,10 +1189,6 @@ private void handleEdsResponse(DiscoveryResponse edsResponse) { } EndpointUpdate.Builder updateBuilder = EndpointUpdate.newBuilder(); updateBuilder.setClusterName(clusterName); - if (assignment.getEndpointsCount() == 0) { - errorMessage = "ClusterLoadAssignment " + clusterName + " : no locality endpoints."; - break; - } Set priorities = new HashSet<>(); int maxPriority = -1; for (io.envoyproxy.envoy.api.v2.endpoint.LocalityLbEndpoints localityLbEndpoints diff --git a/xds/src/test/java/io/grpc/xds/LocalityStoreTest.java b/xds/src/test/java/io/grpc/xds/LocalityStoreTest.java index dbeae1e771b..52c11b4cc8c 100644 --- a/xds/src/test/java/io/grpc/xds/LocalityStoreTest.java +++ b/xds/src/test/java/io/grpc/xds/LocalityStoreTest.java @@ -58,6 +58,7 @@ import io.grpc.LoadBalancerProvider; import io.grpc.LoadBalancerRegistry; import io.grpc.Status; +import io.grpc.Status.Code; import io.grpc.SynchronizationContext; import io.grpc.internal.FakeClock; import io.grpc.internal.FakeClock.ScheduledTask; @@ -924,6 +925,16 @@ public PickResult pickSubchannel(PickSubchannelArgs args) { } } + @Test + public void updateLocalityStore_emptyEndpoints() { + localityStore.updateLocalityStore(Collections.emptyMap()); + assertThat(loadBalancers).hasSize(0); + ArgumentCaptor pickerCaptor = ArgumentCaptor.forClass(null); + verify(helper).updateBalancingState(eq(TRANSIENT_FAILURE), pickerCaptor.capture()); + PickResult result = pickerCaptor.getValue().pickSubchannel(mock(PickSubchannelArgs.class)); + assertThat(result.getStatus().getCode()).isEqualTo(Code.UNAVAILABLE); + } + @Test public void reset() { LocalityLbEndpoints localityInfo1 = diff --git a/xds/src/test/java/io/grpc/xds/XdsClientImplTest.java b/xds/src/test/java/io/grpc/xds/XdsClientImplTest.java index 0b80a9ea92a..326e08bf3a4 100644 --- a/xds/src/test/java/io/grpc/xds/XdsClientImplTest.java +++ b/xds/src/test/java/io/grpc/xds/XdsClientImplTest.java @@ -2042,6 +2042,27 @@ public void edsResponseWithMatchingResource() { 2, true)), 1, 0), new Locality("region3", "zone3", "subzone3"), new LocalityLbEndpoints(ImmutableList.of(), 2, 1)); + + clusterLoadAssignments = ImmutableList.of( + Any.pack(buildClusterLoadAssignment("cluster-foo.googleapis.com", + // 0 locality + ImmutableList.of(), + ImmutableList.of()))); + response = + buildDiscoveryResponse( + "1", clusterLoadAssignments, XdsClientImpl.ADS_TYPE_URL_EDS, "0001"); + responseObserver.onNext(response); + + // Client sent an ACK EDS request. + verify(requestObserver) + .onNext(eq(buildDiscoveryRequest(NODE, "1", "cluster-foo.googleapis.com", + XdsClientImpl.ADS_TYPE_URL_EDS, "0001"))); + + verify(endpointWatcher, times(2)).onEndpointChanged(endpointUpdateCaptor.capture()); + endpointUpdate = endpointUpdateCaptor.getValue(); + assertThat(endpointUpdate.getClusterName()).isEqualTo("cluster-foo.googleapis.com"); + assertThat(endpointUpdate.getDropPolicies()).isEmpty(); + assertThat(endpointUpdate.getLocalityLbEndpointsMap()).isEmpty(); } @Test From 4974b51c53aa6e24dd8bb2db1fa69db8cc171721 Mon Sep 17 00:00:00 2001 From: Jihun Cho Date: Fri, 10 Apr 2020 10:52:13 -0700 Subject: [PATCH 007/104] rls: LruCache interface and implementation (#6799) --- .../grpc/rls/internal/LinkedHashLruCache.java | 386 ++++++++++++++++++ .../java/io/grpc/rls/internal/LruCache.java | 95 +++++ .../rls/internal/LinkedHashLruCacheTest.java | 255 ++++++++++++ 3 files changed, 736 insertions(+) create mode 100644 rls/src/main/java/io/grpc/rls/internal/LinkedHashLruCache.java create mode 100644 rls/src/main/java/io/grpc/rls/internal/LruCache.java create mode 100644 rls/src/test/java/io/grpc/rls/internal/LinkedHashLruCacheTest.java diff --git a/rls/src/main/java/io/grpc/rls/internal/LinkedHashLruCache.java b/rls/src/main/java/io/grpc/rls/internal/LinkedHashLruCache.java new file mode 100644 index 00000000000..b2a122e0508 --- /dev/null +++ b/rls/src/main/java/io/grpc/rls/internal/LinkedHashLruCache.java @@ -0,0 +1,386 @@ +/* + * Copyright 2020 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.rls.internal; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +import com.google.common.base.MoreObjects; +import io.grpc.internal.TimeProvider; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; +import javax.annotation.CheckReturnValue; +import javax.annotation.Nullable; +import javax.annotation.concurrent.GuardedBy; +import javax.annotation.concurrent.ThreadSafe; + +/** + * A LinkedHashLruCache implements least recently used caching where it supports access order lru + * cache eviction while allowing entry level expiration time. When the cache reaches max capacity, + * LruCache try to remove up to one already expired entries. If it doesn't find any expired entries, + * it will remove based on access order of entry. On top of this, LruCache also proactively removes + * expired entries based on configured time interval. + */ +@ThreadSafe +abstract class LinkedHashLruCache implements LruCache { + + private final Object lock = new Object(); + + @GuardedBy("lock") + private final LinkedHashMap delegate; + private final PeriodicCleaner periodicCleaner; + private final TimeProvider timeProvider; + private final EvictionListener evictionListener; + private final AtomicLong estimatedSizeBytes = new AtomicLong(); + private long estimatedMaxSizeBytes; + + LinkedHashLruCache( + final long estimatedMaxSizeBytes, + @Nullable final EvictionListener evictionListener, + int cleaningInterval, + TimeUnit cleaningIntervalUnit, + ScheduledExecutorService ses, + final TimeProvider timeProvider) { + checkState(estimatedMaxSizeBytes > 0, "max estimated cache size should be positive"); + this.estimatedMaxSizeBytes = estimatedMaxSizeBytes; + this.evictionListener = new SizeHandlingEvictionListener(evictionListener); + this.timeProvider = checkNotNull(timeProvider, "timeProvider"); + delegate = new LinkedHashMap( + // rough estimate or minimum hashmap default + Math.max((int) (estimatedMaxSizeBytes / 1000), 16), + /* loadFactor= */ 0.75f, + /* accessOrder= */ true) { + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + if (estimatedSizeBytes.get() <= LinkedHashLruCache.this.estimatedMaxSizeBytes) { + return false; + } + + // first, remove at most 1 expired entry + boolean removed = cleanupExpiredEntries(1, timeProvider.currentTimeNanos()); + // handles size based eviction if necessary no expired entry + boolean shouldRemove = + !removed && shouldInvalidateEldestEntry(eldest.getKey(), eldest.getValue().value); + if (shouldRemove) { + // remove entry by us to make sure lruIterator and cache is in sync + LinkedHashLruCache.this.invalidate(eldest.getKey(), EvictionType.SIZE); + } + return false; + } + }; + periodicCleaner = new PeriodicCleaner(ses, cleaningInterval, cleaningIntervalUnit).start(); + } + + /** + * Determines if the eldest entry should be kept or not when the cache size limit is reached. Note + * that LruCache is access level and the eldest is determined by access pattern. + */ + @SuppressWarnings("unused") + protected boolean shouldInvalidateEldestEntry(K eldestKey, V eldestValue) { + return true; + } + + /** Determines if the entry is already expired or not. */ + protected abstract boolean isExpired(K key, V value, long nowNanos); + + /** + * Returns estimated size of entry to keep track. If it always returns 1, the max size bytes + * behaves like max number of entry (default behavior). + */ + @SuppressWarnings("unused") + protected int estimateSizeOf(K key, V value) { + return 1; + } + + /** Updates size for given key if entry exists. It is useful if the cache value is mutated. */ + public void updateEntrySize(K key) { + synchronized (lock) { + SizedValue entry = readInternal(key); + if (entry == null) { + return; + } + int prevSize = entry.size; + int newSize = estimateSizeOf(key, entry.value); + entry.size = newSize; + estimatedSizeBytes.addAndGet(newSize - prevSize); + } + } + + @Override + @Nullable + public final V cache(K key, V value) { + checkNotNull(key, "key"); + checkNotNull(value, "value"); + SizedValue existing; + int size = estimateSizeOf(key, value); + synchronized (lock) { + estimatedSizeBytes.addAndGet(size); + existing = delegate.put(key, new SizedValue(size, value)); + if (existing != null) { + evictionListener.onEviction(key, existing, EvictionType.REPLACED); + } + } + return existing == null ? null : existing.value; + } + + @Override + @Nullable + @CheckReturnValue + public final V read(K key) { + SizedValue entry = readInternal(key); + if (entry != null) { + return entry.value; + } + return null; + } + + @Nullable + @CheckReturnValue + private SizedValue readInternal(K key) { + checkNotNull(key, "key"); + synchronized (lock) { + SizedValue existing = delegate.get(key); + if (existing != null && isExpired(key, existing.value, timeProvider.currentTimeNanos())) { + invalidate(key, EvictionType.EXPIRED); + return null; + } + return existing; + } + } + + @Override + @Nullable + public final V invalidate(K key) { + return invalidate(key, EvictionType.EXPLICIT); + } + + @Nullable + private V invalidate(K key, EvictionType cause) { + checkNotNull(key, "key"); + checkNotNull(cause, "cause"); + synchronized (lock) { + SizedValue existing = delegate.remove(key); + if (existing != null) { + evictionListener.onEviction(key, existing, cause); + } + return existing == null ? null : existing.value; + } + } + + @Override + public final void invalidateAll(Iterable keys) { + checkNotNull(keys, "keys"); + synchronized (lock) { + for (K key : keys) { + SizedValue existing = delegate.remove(key); + if (existing != null) { + evictionListener.onEviction(key, existing, EvictionType.EXPLICIT); + } + } + } + } + + @Override + @CheckReturnValue + public final boolean hasCacheEntry(K key) { + // call readInternal to filter already expired entry in the cache + return readInternal(key) != null; + } + + /** Returns shallow copied values in the cache. */ + public final List values() { + synchronized (lock) { + List list = new ArrayList<>(delegate.size()); + for (SizedValue value : delegate.values()) { + list.add(value.value); + } + return Collections.unmodifiableList(list); + } + } + + /** + * Resizes cache. If new size is smaller than current estimated size, it will free up space by + * removing expired entries and removing oldest entries by LRU order. + */ + public final void resize(int newSizeBytes) { + long now = timeProvider.currentTimeNanos(); + synchronized (lock) { + long estimatedSizeBytesCopy = estimatedMaxSizeBytes; + this.estimatedMaxSizeBytes = newSizeBytes; + if (estimatedSizeBytesCopy <= newSizeBytes) { + // new size is larger no need to do cleanup + return; + } + // cleanup expired entries + cleanupExpiredEntries(now); + + // cleanup eldest entry until new size limit + Iterator> lruIter = delegate.entrySet().iterator(); + while (lruIter.hasNext() && estimatedMaxSizeBytes > this.estimatedSizeBytes.get()) { + Map.Entry entry = lruIter.next(); + lruIter.remove(); + // eviction listener will update the estimatedSizeBytes + evictionListener.onEviction(entry.getKey(), entry.getValue(), EvictionType.SIZE); + } + } + } + + @Override + @CheckReturnValue + public final int estimatedSize() { + synchronized (lock) { + return delegate.size(); + } + } + + private boolean cleanupExpiredEntries(long now) { + return cleanupExpiredEntries(Integer.MAX_VALUE, now); + } + + // maxExpiredEntries is by number of entries + private boolean cleanupExpiredEntries(int maxExpiredEntries, long now) { + checkArgument(maxExpiredEntries > 0, "maxExpiredEntries must be positive"); + boolean removedAny = false; + synchronized (lock) { + Iterator> lruIter = delegate.entrySet().iterator(); + while (lruIter.hasNext() && maxExpiredEntries > 0) { + Map.Entry entry = lruIter.next(); + if (isExpired(entry.getKey(), entry.getValue().value, now)) { + lruIter.remove(); + evictionListener.onEviction(entry.getKey(), entry.getValue(), EvictionType.EXPIRED); + removedAny = true; + maxExpiredEntries--; + } + } + } + return removedAny; + } + + @Override + public final void close() { + synchronized (lock) { + periodicCleaner.stop(); + doClose(); + delegate.clear(); + } + } + + protected void doClose() {} + + /** Periodically cleans up the AsyncRequestCache. */ + private final class PeriodicCleaner { + + private final ScheduledExecutorService ses; + private final int interval; + private final TimeUnit intervalUnit; + private ScheduledFuture scheduledFuture; + + PeriodicCleaner(ScheduledExecutorService ses, int interval, TimeUnit intervalUnit) { + this.ses = checkNotNull(ses, "ses"); + checkState(interval > 0, "interval must be positive"); + this.interval = interval; + this.intervalUnit = checkNotNull(intervalUnit, "intervalUnit"); + } + + PeriodicCleaner start() { + checkState(scheduledFuture == null, "cleaning task can be started only once"); + this.scheduledFuture = + ses.scheduleAtFixedRate(new CleaningTask(), interval, interval, intervalUnit); + return this; + } + + void stop() { + if (scheduledFuture != null) { + scheduledFuture.cancel(false); + scheduledFuture = null; + } + } + + private class CleaningTask implements Runnable { + + @Override + public void run() { + cleanupExpiredEntries(timeProvider.currentTimeNanos()); + } + } + } + + /** A {@link EvictionListener} keeps track of size. */ + private final class SizeHandlingEvictionListener implements EvictionListener { + + private final EvictionListener delegate; + + SizeHandlingEvictionListener(@Nullable EvictionListener delegate) { + this.delegate = delegate; + } + + @Override + public void onEviction(K key, SizedValue value, EvictionType cause) { + estimatedSizeBytes.addAndGet(-1 * value.size); + if (delegate != null) { + delegate.onEviction(key, value.value, cause); + } + } + } + + private final class SizedValue { + volatile int size; + final V value; + + SizedValue(int size, V value) { + this.size = size; + this.value = value; + } + + @Override + public boolean equals(Object o) { + // NOTE: the size doesn't affect equality + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + LinkedHashLruCache.SizedValue that = (LinkedHashLruCache.SizedValue) o; + return Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + // NOTE: the size doesn't affect hashCode + return Objects.hash(value); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("size", size) + .add("value", value) + .toString(); + } + } +} diff --git a/rls/src/main/java/io/grpc/rls/internal/LruCache.java b/rls/src/main/java/io/grpc/rls/internal/LruCache.java new file mode 100644 index 00000000000..07909919f11 --- /dev/null +++ b/rls/src/main/java/io/grpc/rls/internal/LruCache.java @@ -0,0 +1,95 @@ +/* + * Copyright 2020 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.rls.internal; + +import javax.annotation.CheckReturnValue; +import javax.annotation.Nullable; + +/** An LruCache is a cache with least recently used eviction. */ +interface LruCache { + + /** + * Populates a cache entry. If the cache entry for given key already exists, the value will be + * replaced to the new value. + * + * @return the previous value associated with key, otherwise {@code null} + */ + @Nullable + V cache(K key, V value); + + /** + * Returns cached value for given key if exists, otherwise {@code null}. This operation doesn't + * return already expired cache entry. + */ + @Nullable + @CheckReturnValue + V read(K key); + + /** + * Invalidates an entry for given key if exists. This operation will trigger {@link + * EvictionListener} with {@link EvictionType#EXPLICIT}. + * + * @return the previous value associated with key, otherwise {@code null} + */ + @Nullable + V invalidate(K key); + + /** + * Invalidates cache entries for given keys. This operation will trigger {@link EvictionListener} + * with {@link EvictionType#EXPLICIT}. + */ + void invalidateAll(Iterable keys); + + /** Returns {@code true} if given key is cached. */ + @CheckReturnValue + boolean hasCacheEntry(K key); + + /** + * Returns the estimated number of entry of the cache. Note that the size can be larger than its + * true size, because there might be already expired cache. + */ + @CheckReturnValue + int estimatedSize(); + + /** Closes underlying resources. */ + void close(); + + /** A Listener notifies cache eviction events. */ + interface EvictionListener { + + /** + * Notifies the listener when any cache entry is evicted. Implementation can assume that this + * method is called serially. Implementation should be non blocking, for long running task + * consider offloading the task to {@link java.util.concurrent.Executor}. + */ + void onEviction(K key, V value, EvictionType cause); + } + + /** Type of cache eviction. */ + enum EvictionType { + /** Explicitly removed by user. */ + EXPLICIT, + /** Evicted due to size limit. */ + SIZE, + /** Evicted due to entry expired. */ + EXPIRED, + /** Removed due to error. */ + ERROR, + /** Evicted by replacement. */ + REPLACED + } +} diff --git a/rls/src/test/java/io/grpc/rls/internal/LinkedHashLruCacheTest.java b/rls/src/test/java/io/grpc/rls/internal/LinkedHashLruCacheTest.java new file mode 100644 index 00000000000..e8252a51099 --- /dev/null +++ b/rls/src/test/java/io/grpc/rls/internal/LinkedHashLruCacheTest.java @@ -0,0 +1,255 @@ +/* + * Copyright 2020 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.rls.internal; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.CALLS_REAL_METHODS; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import io.grpc.internal.TimeProvider; +import io.grpc.rls.internal.LruCache.EvictionListener; +import io.grpc.rls.internal.LruCache.EvictionType; +import java.util.Objects; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +@RunWith(JUnit4.class) +public class LinkedHashLruCacheTest { + + private static final int MAX_SIZE = 5; + + @Rule + public final MockitoRule mocks = MockitoJUnit.rule(); + + private final DoNotUseFakeScheduledService fakeScheduledService = + mock(DoNotUseFakeScheduledService.class, CALLS_REAL_METHODS); + private final TimeProvider timeProvider = fakeScheduledService.getFakeTicker(); + + @Mock + private EvictionListener evictionListener; + private LinkedHashLruCache cache; + + @Before + public void setUp() { + this.cache = new LinkedHashLruCache( + MAX_SIZE, + evictionListener, + 10, + TimeUnit.NANOSECONDS, + fakeScheduledService, + timeProvider) { + @Override + protected boolean isExpired(Integer key, Entry value, long nowNanos) { + return value.expireTime <= nowNanos; + } + }; + } + + @Test + public void eviction_size() { + for (int i = 1; i <= MAX_SIZE; i++) { + cache.cache(i, new Entry("Entry" + i, Long.MAX_VALUE)); + } + cache.cache(MAX_SIZE + 1, new Entry("should kick the first", Long.MAX_VALUE)); + + verify(evictionListener).onEviction(1, new Entry("Entry1", Long.MAX_VALUE), EvictionType.SIZE); + assertThat(cache.estimatedSize()).isEqualTo(MAX_SIZE); + } + + @Test + public void size() { + Entry entry1 = new Entry("Entry0", timeProvider.currentTimeNanos() + 10); + Entry entry2 = new Entry("Entry1", timeProvider.currentTimeNanos() + 20); + cache.cache(0, entry1); + cache.cache(1, entry2); + assertThat(cache.estimatedSize()).isEqualTo(2); + + assertThat(cache.invalidate(0)).isEqualTo(entry1); + assertThat(cache.estimatedSize()).isEqualTo(1); + + assertThat(cache.invalidate(1)).isEqualTo(entry2); + assertThat(cache.estimatedSize()).isEqualTo(0); + } + + @Test + public void eviction_expire() { + Entry toBeEvicted = new Entry("Entry0", timeProvider.currentTimeNanos() + 10); + Entry survivor = new Entry("Entry1", timeProvider.currentTimeNanos() + 20); + cache.cache(0, toBeEvicted); + cache.cache(1, survivor); + + fakeScheduledService.advance(10, TimeUnit.NANOSECONDS); + verify(evictionListener).onEviction(0, toBeEvicted, EvictionType.EXPIRED); + + fakeScheduledService.advance(10, TimeUnit.NANOSECONDS); + verify(evictionListener).onEviction(1, survivor, EvictionType.EXPIRED); + } + + @Test + public void eviction_explicit() { + Entry toBeEvicted = new Entry("Entry0", timeProvider.currentTimeNanos() + 10); + Entry survivor = new Entry("Entry1", timeProvider.currentTimeNanos() + 20); + cache.cache(0, toBeEvicted); + cache.cache(1, survivor); + + assertThat(cache.invalidate(0)).isEqualTo(toBeEvicted); + + verify(evictionListener).onEviction(0, toBeEvicted, EvictionType.EXPLICIT); + } + + @Test + public void eviction_replaced() { + Entry toBeEvicted = new Entry("Entry0", timeProvider.currentTimeNanos() + 10); + Entry survivor = new Entry("Entry1", timeProvider.currentTimeNanos() + 20); + cache.cache(0, toBeEvicted); + cache.cache(0, survivor); + + verify(evictionListener).onEviction(0, toBeEvicted, EvictionType.REPLACED); + } + + @Test + public void eviction_size_shouldEvictAlreadyExpired() { + for (int i = 1; i <= MAX_SIZE; i++) { + // last two entries are <= current time (already expired) + cache.cache(i, new Entry("Entry" + i, timeProvider.currentTimeNanos() + MAX_SIZE - i - 1)); + } + cache.cache(MAX_SIZE + 1, new Entry("should kick the first", Long.MAX_VALUE)); + + // should remove MAX_SIZE-1 instead of MAX_SIZE because MAX_SIZE is accessed later + verify(evictionListener) + .onEviction(eq(MAX_SIZE - 1), any(Entry.class), eq(EvictionType.EXPIRED)); + assertThat(cache.estimatedSize()).isEqualTo(MAX_SIZE); + } + + @Test + public void eviction_get_shouldNotReturnAlreadyExpired() { + for (int i = 1; i <= MAX_SIZE; i++) { + // last entry is already expired when added + cache.cache(i, new Entry("Entry" + i, timeProvider.currentTimeNanos() + MAX_SIZE - i)); + } + + assertThat(cache.estimatedSize()).isEqualTo(MAX_SIZE); + assertThat(cache.read(MAX_SIZE)).isNull(); + assertThat(cache.estimatedSize()).isEqualTo(MAX_SIZE - 1); + verify(evictionListener).onEviction(eq(MAX_SIZE), any(Entry.class), eq(EvictionType.EXPIRED)); + } + + private static final class Entry { + String value; + long expireTime; + + Entry(String value, long expireTime) { + this.value = value; + this.expireTime = expireTime; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Entry entry = (Entry) o; + return expireTime == entry.expireTime && Objects.equals(value, entry.value); + } + + @Override + public int hashCode() { + return Objects.hash(value, expireTime); + } + } + + /** + * A fake minimal implementation of ScheduledExecutorService *only* supports scheduledAtFixedRate + * with a lot of limitation / assumptions. Only intended to be used in this test with + * CALL_REAL_METHODS mock. + */ + private abstract static class DoNotUseFakeScheduledService implements ScheduledExecutorService { + + private long currTimeNanos; + private long period; + private long nextRun; + private AtomicReference command; + + @Override + public final ScheduledFuture scheduleAtFixedRate( + Runnable command, long initialDelay, long period, TimeUnit unit) { + // hack to initialize + if (this.command == null) { + this.command = new AtomicReference<>(); + } + checkState(this.command.get() == null, "only can schedule one"); + checkState(period > 0, "period should be positive"); + checkState(initialDelay >= 0, "initial delay should be >= 0"); + if (initialDelay == 0) { + initialDelay = period; + command.run(); + } + this.command.set(checkNotNull(command, "command")); + this.nextRun = checkNotNull(unit, "unit").toNanos(initialDelay) + currTimeNanos; + this.period = unit.toNanos(period); + return mock(ScheduledFuture.class); + } + + TimeProvider getFakeTicker() { + return new TimeProvider() { + @Override + public long currentTimeNanos() { + return currTimeNanos; + } + }; + } + + void advance(long delta, TimeUnit unit) { + // if scheduled command, only can advance the ticker to trigger at most 1 event + boolean scheduled = command != null && command.get() != null; + long deltaNanos = unit.toNanos(delta); + if (scheduled) { + checkArgument( + (this.currTimeNanos + deltaNanos) < (nextRun + 2 * period), + "Cannot advance ticker because more than one repeated tasks will run"); + long finalTime = this.currTimeNanos + deltaNanos; + if (finalTime >= nextRun) { + nextRun += period; + this.currTimeNanos = nextRun; + command.get().run(); + } + this.currTimeNanos = finalTime; + } else { + this.currTimeNanos += deltaNanos; + } + } + } +} From 9ead606b84dcad33825e9daf570ad50ffb8bfcc4 Mon Sep 17 00:00:00 2001 From: Eric Anderson Date: Fri, 10 Apr 2020 12:03:23 -0700 Subject: [PATCH 008/104] netty: Reduce race window size between GOAWAY and new streams MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The race between new streams and transport shutdown is #2562, but it is still far from being generally solved. This reduces the race window of new streams from (transport selection → stream created on network thread) to (transport selection → stream enqueued on network thread). Since only a single thread now needs to do work in the stream creation race window, the window should be dramatically smaller. This only reduces GOAWAY races when the server performs a graceful shutdown (using two GOAWAYs), as that is the only non-racy way on-the-wire to shutdown a connection in HTTP/2. --- .../netty/ClientTransportLifecycleManager.java | 15 +++++++++++++-- .../java/io/grpc/netty/NettyClientHandler.java | 13 ++++++++++++- netty/src/main/java/io/grpc/netty/WriteQueue.java | 13 +++++++++++++ .../io/grpc/netty/NettyClientHandlerTest.java | 13 +++++++++++++ .../java/io/grpc/netty/NettyHandlerTestBase.java | 4 ++++ 5 files changed, 55 insertions(+), 3 deletions(-) diff --git a/netty/src/main/java/io/grpc/netty/ClientTransportLifecycleManager.java b/netty/src/main/java/io/grpc/netty/ClientTransportLifecycleManager.java index e9f70405645..e10150cd134 100644 --- a/netty/src/main/java/io/grpc/netty/ClientTransportLifecycleManager.java +++ b/netty/src/main/java/io/grpc/netty/ClientTransportLifecycleManager.java @@ -43,14 +43,25 @@ public void notifyReady() { listener.transportReady(); } - public void notifyShutdown(Status s) { + /** + * Marks transport as shutdown, but does not set the error status. This must eventually be + * followed by a call to notifyShutdown. + */ + public void notifyGracefulShutdown(Status s) { if (transportShutdown) { return; } transportShutdown = true; + listener.transportShutdown(s); + } + + public void notifyShutdown(Status s) { + notifyGracefulShutdown(s); + if (shutdownStatus != null) { + return; + } shutdownStatus = s; shutdownThrowable = s.asException(); - listener.transportShutdown(s); } public void notifyInUse(boolean inUse) { diff --git a/netty/src/main/java/io/grpc/netty/NettyClientHandler.java b/netty/src/main/java/io/grpc/netty/NettyClientHandler.java index f04b3e6cbf6..677358f4fef 100644 --- a/netty/src/main/java/io/grpc/netty/NettyClientHandler.java +++ b/netty/src/main/java/io/grpc/netty/NettyClientHandler.java @@ -755,10 +755,21 @@ public boolean visit(Http2Stream stream) throws Http2Exception { /** * Handler for a GOAWAY being received. Fails any streams created after the - * last known stream. + * last known stream. May only be called during a read. */ private void goingAway(Status status) { + lifecycleManager.notifyGracefulShutdown(status); + // Try to allocate as many in-flight streams as possible, to reduce race window of + // https://github.com/grpc/grpc-java/issues/2562 . To be of any help, the server has to + // gracefully shut down the connection with two GOAWAYs. gRPC servers generally send a PING + // after the first GOAWAY, so they can very precisely detect when the GOAWAY has been + // processed and thus this processing must be in-line before processing additional reads. + + // This can cause reentrancy, but should be minor since it is normal to handle writes in + // response to a read. Also, the call stack is rather shallow at this point + clientWriteQueue.drainNow(); lifecycleManager.notifyShutdown(status); + final Status goAwayStatus = lifecycleManager.getShutdownStatus(); final int lastKnownStream = connection().local().lastStreamKnownByPeer(); try { diff --git a/netty/src/main/java/io/grpc/netty/WriteQueue.java b/netty/src/main/java/io/grpc/netty/WriteQueue.java index cfbfe758d65..f80e3fcaa5b 100644 --- a/netty/src/main/java/io/grpc/netty/WriteQueue.java +++ b/netty/src/main/java/io/grpc/netty/WriteQueue.java @@ -101,6 +101,19 @@ void enqueue(Runnable runnable, boolean flush) { } } + /** + * Executes enqueued work directly on the current thread. This can be used to trigger writes + * before performing additional reads. Must be called from the event loop. This method makes no + * guarantee that the work queue is empty when it returns. + */ + void drainNow() { + Preconditions.checkState(channel.eventLoop().inEventLoop(), "must be on the event loop"); + if (queue.peek() == null) { + return; + } + flush(); + } + /** * Process the queue of commands and dispatch them to the stream. This method is only * called in the event loop diff --git a/netty/src/test/java/io/grpc/netty/NettyClientHandlerTest.java b/netty/src/test/java/io/grpc/netty/NettyClientHandlerTest.java index dfdfcf4f9c8..c5f81325bd5 100644 --- a/netty/src/test/java/io/grpc/netty/NettyClientHandlerTest.java +++ b/netty/src/test/java/io/grpc/netty/NettyClientHandlerTest.java @@ -361,6 +361,19 @@ public void receivedGoAwayShouldNotAffectEarlyStreamId() throws Exception { assertTrue(future.isDone()); } + @Test + public void receivedGoAwayShouldNotAffectRacingQueuedStreamId() throws Exception { + // This command has not actually been executed yet + ChannelFuture future = writeQueue().enqueue( + newCreateStreamCommand(grpcHeaders, streamTransportState), true); + channelRead(goAwayFrame(streamId)); + verify(streamListener, never()) + .closed(any(Status.class), any(Metadata.class)); + verify(streamListener, never()) + .closed(any(Status.class), any(RpcProgress.class), any(Metadata.class)); + assertTrue(future.isDone()); + } + @Test public void receivedResetWithRefuseCode() throws Exception { ChannelFuture future = enqueue(newCreateStreamCommand(grpcHeaders, streamTransportState)); diff --git a/netty/src/test/java/io/grpc/netty/NettyHandlerTestBase.java b/netty/src/test/java/io/grpc/netty/NettyHandlerTestBase.java index 1e0870460e3..fab3c767923 100644 --- a/netty/src/test/java/io/grpc/netty/NettyHandlerTestBase.java +++ b/netty/src/test/java/io/grpc/netty/NettyHandlerTestBase.java @@ -203,6 +203,10 @@ public int compareTo(Delayed o) { } } + protected final WriteQueue writeQueue() { + return writeQueue; + } + protected final T handler() { return handler; } From cfe73eb4846f222b87f63d168b6b030a82506182 Mon Sep 17 00:00:00 2001 From: ashithasantosh <55257063+ashithasantosh@users.noreply.github.com> Date: Fri, 10 Apr 2020 13:38:46 -0700 Subject: [PATCH 009/104] Implemented Frame Size Negotiation in ALTS for gRPC Java. (#6840) --- .../alts/internal/AltsHandshakerClient.java | 2 ++ .../alts/internal/AltsTsiFrameProtector.java | 23 +++++++++++-------- .../grpc/alts/internal/AltsTsiHandshaker.java | 14 ++++++++++- .../internal/AltsHandshakerClientTest.java | 17 ++++++++++++++ .../internal/AltsTsiFrameProtectorTest.java | 4 ++-- .../grpc/alts/internal/FakeTsiHandshaker.java | 2 +- 6 files changed, 49 insertions(+), 13 deletions(-) diff --git a/alts/src/main/java/io/grpc/alts/internal/AltsHandshakerClient.java b/alts/src/main/java/io/grpc/alts/internal/AltsHandshakerClient.java index e9103963780..083ad056789 100644 --- a/alts/src/main/java/io/grpc/alts/internal/AltsHandshakerClient.java +++ b/alts/src/main/java/io/grpc/alts/internal/AltsHandshakerClient.java @@ -82,6 +82,7 @@ private void setStartClientFields(HandshakerReq.Builder req) { startClientReq.addTargetIdentitiesBuilder().setServiceAccount(serviceAccount); } } + startClientReq.setMaxFrameSize(AltsTsiFrameProtector.getMaxFrameSize()); req.setClientStart(startClientReq); } @@ -97,6 +98,7 @@ private void setStartServerFields(HandshakerReq.Builder req, ByteBuffer inBytes) if (handshakerOptions.getRpcProtocolVersions() != null) { startServerReq.setRpcVersions(handshakerOptions.getRpcProtocolVersions()); } + startServerReq.setMaxFrameSize(AltsTsiFrameProtector.getMaxFrameSize()); req.setServerStart(startServerReq); } diff --git a/alts/src/main/java/io/grpc/alts/internal/AltsTsiFrameProtector.java b/alts/src/main/java/io/grpc/alts/internal/AltsTsiFrameProtector.java index 23e1dc9e5ad..67d6637a130 100644 --- a/alts/src/main/java/io/grpc/alts/internal/AltsTsiFrameProtector.java +++ b/alts/src/main/java/io/grpc/alts/internal/AltsTsiFrameProtector.java @@ -33,9 +33,10 @@ public final class AltsTsiFrameProtector implements TsiFrameProtector { private static final int HEADER_TYPE_FIELD_BYTES = 4; private static final int HEADER_BYTES = HEADER_LEN_FIELD_BYTES + HEADER_TYPE_FIELD_BYTES; private static final int HEADER_TYPE_DEFAULT = 6; - // Total frame size including full header and tag. - private static final int MAX_ALLOWED_FRAME_BYTES = 16 * 1024; - private static final int LIMIT_MAX_ALLOWED_FRAME_BYTES = 1024 * 1024; + private static final int LIMIT_MAX_ALLOWED_FRAME_SIZE = 1024 * 1024; + // Frame size negotiation extends frame size range to [MIN_FRAME_SIZE, MAX_FRAME_SIZE]. + private static final int MIN_FRAME_SIZE = 16 * 1024; + private static final int MAX_FRAME_SIZE = 128 * 1024; private final Protector protector; private final Unprotector unprotector; @@ -44,7 +45,7 @@ public final class AltsTsiFrameProtector implements TsiFrameProtector { public AltsTsiFrameProtector( int maxProtectedFrameBytes, ChannelCrypterNetty crypter, ByteBufAllocator alloc) { checkArgument(maxProtectedFrameBytes > HEADER_BYTES + crypter.getSuffixLength()); - maxProtectedFrameBytes = Math.min(LIMIT_MAX_ALLOWED_FRAME_BYTES, maxProtectedFrameBytes); + maxProtectedFrameBytes = Math.min(LIMIT_MAX_ALLOWED_FRAME_SIZE, maxProtectedFrameBytes); protector = new Protector(maxProtectedFrameBytes, crypter); unprotector = new Unprotector(crypter, alloc); } @@ -65,12 +66,16 @@ static int getHeaderTypeDefault() { return HEADER_TYPE_DEFAULT; } - public static int getMaxAllowedFrameBytes() { - return MAX_ALLOWED_FRAME_BYTES; + static int getLimitMaxAllowedFrameSize() { + return LIMIT_MAX_ALLOWED_FRAME_SIZE; } - static int getLimitMaxAllowedFrameBytes() { - return LIMIT_MAX_ALLOWED_FRAME_BYTES; + public static int getMinFrameSize() { + return MIN_FRAME_SIZE; + } + + public static int getMaxFrameSize() { + return MAX_FRAME_SIZE; } @Override @@ -262,7 +267,7 @@ private void handleHeader() { checkArgument( requiredProtectedBytes >= suffixBytes, "Invalid header field: frame size too small"); checkArgument( - requiredProtectedBytes <= LIMIT_MAX_ALLOWED_FRAME_BYTES - HEADER_BYTES, + requiredProtectedBytes <= LIMIT_MAX_ALLOWED_FRAME_SIZE - HEADER_BYTES, "Invalid header field: frame size too large"); int frameType = header.readIntLE(); checkArgument(frameType == HEADER_TYPE_DEFAULT, "Invalid header field: frame type"); diff --git a/alts/src/main/java/io/grpc/alts/internal/AltsTsiHandshaker.java b/alts/src/main/java/io/grpc/alts/internal/AltsTsiHandshaker.java index 3cd639ad5ff..21824a068c5 100644 --- a/alts/src/main/java/io/grpc/alts/internal/AltsTsiHandshaker.java +++ b/alts/src/main/java/io/grpc/alts/internal/AltsTsiHandshaker.java @@ -26,11 +26,15 @@ import java.security.GeneralSecurityException; import java.util.ArrayList; import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; /** * Negotiates a grpc channel key to be used by the TsiFrameProtector, using ALTs handshaker service. */ public final class AltsTsiHandshaker implements TsiHandshaker { + private static final Logger logger = Logger.getLogger(AltsTsiHandshaker.class.getName()); + public static final String TSI_SERVICE_ACCOUNT_PEER_PROPERTY = "service_account"; private final boolean isClient; @@ -178,6 +182,14 @@ public TsiFrameProtector createFrameProtector(int maxFrameSize, ByteBufAllocator byte[] key = handshaker.getKey(); Preconditions.checkState(key.length == AltsChannelCrypter.getKeyLength(), "Bad key length."); + // Frame size negotiation is not performed if the peer does not send max frame size (e.g. peer + // is gRPC Go or peer uses an old binary). + int peerMaxFrameSize = handshaker.getResult().getMaxFrameSize(); + if (peerMaxFrameSize != 0) { + maxFrameSize = Math.min(peerMaxFrameSize, AltsTsiFrameProtector.getMaxFrameSize()); + maxFrameSize = Math.max(AltsTsiFrameProtector.getMinFrameSize(), maxFrameSize); + } + logger.log(Level.INFO, "Maximum frame size value is " + maxFrameSize); return new AltsTsiFrameProtector(maxFrameSize, new AltsChannelCrypter(key, isClient), alloc); } @@ -190,7 +202,7 @@ public TsiFrameProtector createFrameProtector(int maxFrameSize, ByteBufAllocator */ @Override public TsiFrameProtector createFrameProtector(ByteBufAllocator alloc) { - return createFrameProtector(AltsTsiFrameProtector.getMaxAllowedFrameBytes(), alloc); + return createFrameProtector(AltsTsiFrameProtector.getMinFrameSize(), alloc); } @Override diff --git a/alts/src/test/java/io/grpc/alts/internal/AltsHandshakerClientTest.java b/alts/src/test/java/io/grpc/alts/internal/AltsHandshakerClientTest.java index 82c8a682bca..d1bd5ffea08 100644 --- a/alts/src/test/java/io/grpc/alts/internal/AltsHandshakerClientTest.java +++ b/alts/src/test/java/io/grpc/alts/internal/AltsHandshakerClientTest.java @@ -106,6 +106,7 @@ public void startClientHandshakeWithOptions() throws Exception { .setTargetName(TEST_TARGET_NAME) .addTargetIdentities( Identity.newBuilder().setServiceAccount(TEST_TARGET_SERVICE_ACCOUNT)) + .setMaxFrameSize(AltsTsiFrameProtector.getMaxFrameSize()) .build()) .build(); verify(mockStub).send(req); @@ -133,6 +134,22 @@ public void startServerHandshakeSuccess() throws Exception { ByteBuffer inBytes = ByteBuffer.allocate(IN_BYTES_SIZE); ByteBuffer outFrame = handshaker.startServerHandshake(inBytes); + HandshakerReq req = + HandshakerReq.newBuilder() + .setServerStart( + StartServerHandshakeReq.newBuilder() + .addApplicationProtocols(AltsHandshakerClient.getApplicationProtocol()) + .putHandshakeParameters( + HandshakeProtocol.ALTS.getNumber(), + ServerHandshakeParameters.newBuilder() + .addRecordProtocols(AltsHandshakerClient.getRecordProtocol()) + .build()) + .setInBytes(ByteString.copyFrom(ByteBuffer.allocate(IN_BYTES_SIZE))) + .setMaxFrameSize(AltsTsiFrameProtector.getMaxFrameSize()) + .build()) + .build(); + verify(mockStub).send(req); + assertEquals(ByteString.copyFrom(outFrame), MockAltsHandshakerResp.getOutFrame()); assertFalse(handshaker.isFinished()); assertNull(handshaker.getResult()); diff --git a/alts/src/test/java/io/grpc/alts/internal/AltsTsiFrameProtectorTest.java b/alts/src/test/java/io/grpc/alts/internal/AltsTsiFrameProtectorTest.java index 4405a99eff0..cbda9dbcdc0 100644 --- a/alts/src/test/java/io/grpc/alts/internal/AltsTsiFrameProtectorTest.java +++ b/alts/src/test/java/io/grpc/alts/internal/AltsTsiFrameProtectorTest.java @@ -125,7 +125,7 @@ public void parserHeader_frameTooLarge() throws GeneralSecurityException { getDirectBuffer( AltsTsiFrameProtector.getHeaderBytes() + FakeChannelCrypter.getTagBytes(), ref); in.writeIntLE( - AltsTsiFrameProtector.getLimitMaxAllowedFrameBytes() + AltsTsiFrameProtector.getLimitMaxAllowedFrameSize() - AltsTsiFrameProtector.getHeaderLenFieldBytes() + 1); in.writeIntLE(6); @@ -206,7 +206,7 @@ public void parserHeader_frameMaxOk() throws GeneralSecurityException { getDirectBuffer( AltsTsiFrameProtector.getHeaderBytes() + FakeChannelCrypter.getTagBytes(), ref); in.writeIntLE( - AltsTsiFrameProtector.getLimitMaxAllowedFrameBytes() + AltsTsiFrameProtector.getLimitMaxAllowedFrameSize() - AltsTsiFrameProtector.getHeaderLenFieldBytes()); in.writeIntLE(6); diff --git a/alts/src/test/java/io/grpc/alts/internal/FakeTsiHandshaker.java b/alts/src/test/java/io/grpc/alts/internal/FakeTsiHandshaker.java index a04bbfd07e8..1483e4b08e2 100644 --- a/alts/src/test/java/io/grpc/alts/internal/FakeTsiHandshaker.java +++ b/alts/src/test/java/io/grpc/alts/internal/FakeTsiHandshaker.java @@ -224,7 +224,7 @@ public TsiFrameProtector createFrameProtector(int maxFrameSize, ByteBufAllocator @Override public TsiFrameProtector createFrameProtector(ByteBufAllocator alloc) { - return createFrameProtector(AltsTsiFrameProtector.getMaxAllowedFrameBytes(), alloc); + return createFrameProtector(AltsTsiFrameProtector.getMinFrameSize(), alloc); } @Override From 1686d703e62ce6231c6e516fed303b7dbe132c63 Mon Sep 17 00:00:00 2001 From: Jihun Cho Date: Fri, 10 Apr 2020 15:56:40 -0700 Subject: [PATCH 010/104] rls: rls request factory (aka key builder map) (#6823) --- .../grpc/rls/internal/RlsRequestFactory.java | 117 +++++++++++++ .../rls/internal/RlsRequestFactoryTest.java | 160 ++++++++++++++++++ 2 files changed, 277 insertions(+) create mode 100644 rls/src/main/java/io/grpc/rls/internal/RlsRequestFactory.java create mode 100644 rls/src/test/java/io/grpc/rls/internal/RlsRequestFactoryTest.java diff --git a/rls/src/main/java/io/grpc/rls/internal/RlsRequestFactory.java b/rls/src/main/java/io/grpc/rls/internal/RlsRequestFactory.java new file mode 100644 index 00000000000..efe977ced3f --- /dev/null +++ b/rls/src/main/java/io/grpc/rls/internal/RlsRequestFactory.java @@ -0,0 +1,117 @@ +/* + * Copyright 2020 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.rls.internal; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.base.MoreObjects; +import com.google.common.collect.HashBasedTable; +import com.google.common.collect.Table; +import io.grpc.Metadata; +import io.grpc.Status; +import io.grpc.StatusRuntimeException; +import io.grpc.rls.internal.RlsProtoData.GrpcKeyBuilder; +import io.grpc.rls.internal.RlsProtoData.GrpcKeyBuilder.Name; +import io.grpc.rls.internal.RlsProtoData.NameMatcher; +import io.grpc.rls.internal.RlsProtoData.RouteLookupConfig; +import io.grpc.rls.internal.RlsProtoData.RouteLookupRequest; +import java.util.HashMap; +import java.util.Map; +import javax.annotation.CheckReturnValue; + +/** + * A RlsRequestFactory creates {@link RouteLookupRequest} using key builder map from {@link + * RouteLookupConfig}. + */ +final class RlsRequestFactory { + + private final String target; + /** + * schema: Path(serviceName/methodName or serviceName/*), rls request headerName, header fields. + */ + private final Table keyBuilderTable; + + /** Constructor. */ + public RlsRequestFactory(RouteLookupConfig rlsConfig) { + checkNotNull(rlsConfig, "rlsConfig"); + this.target = rlsConfig.getLookupService(); + this.keyBuilderTable = createKeyBuilderTable(rlsConfig); + } + + private static Table createKeyBuilderTable( + RouteLookupConfig config) { + Table table = HashBasedTable.create(); + for (GrpcKeyBuilder grpcKeyBuilder : config.getGrpcKeyBuilders()) { + for (NameMatcher nameMatcher : grpcKeyBuilder.getHeaders()) { + for (Name name : grpcKeyBuilder.getNames()) { + String method = + name.getMethod() == null || name.getMethod().isEmpty() + ? "*" : name.getMethod(); + String path = name.getService() + "/" + method; + table.put(path, nameMatcher.getKey(), nameMatcher); + } + } + } + return table; + } + + /** Creates a {@link RouteLookupRequest} for given request's metadata. */ + @CheckReturnValue + public RouteLookupRequest create(String service, String method, Metadata metadata) { + checkNotNull(service, "service"); + checkNotNull(method, "method"); + String path = service + "/" + method; + Map keyBuilder = keyBuilderTable.row(path); + // if no matching keyBuilder found, fall back to wildcard match (ServiceName/*) + if (keyBuilder.isEmpty()) { + keyBuilder = keyBuilderTable.row(service + "/*"); + } + Map rlsRequestHeaders = createRequestHeaders(metadata, keyBuilder); + return new RouteLookupRequest(target, path, "grpc", rlsRequestHeaders); + } + + private Map createRequestHeaders( + Metadata metadata, Map keyBuilder) { + Map rlsRequestHeaders = new HashMap<>(); + for (Map.Entry entry : keyBuilder.entrySet()) { + NameMatcher nameMatcher = entry.getValue(); + String value = null; + for (String requestHeaderName : nameMatcher.names()) { + value = metadata.get(Metadata.Key.of(requestHeaderName, Metadata.ASCII_STRING_MARSHALLER)); + if (value != null) { + break; + } + } + if (value != null) { + rlsRequestHeaders.put(entry.getKey(), value); + } else if (!nameMatcher.isOptional()) { + throw new StatusRuntimeException( + Status.INVALID_ARGUMENT.withDescription( + String.format("Missing mandatory metadata(%s) not found", entry.getKey()))); + } + } + return rlsRequestHeaders; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("lookupService", target) + .add("keyBuilderTable", keyBuilderTable) + .toString(); + } +} diff --git a/rls/src/test/java/io/grpc/rls/internal/RlsRequestFactoryTest.java b/rls/src/test/java/io/grpc/rls/internal/RlsRequestFactoryTest.java new file mode 100644 index 00000000000..cac258d0986 --- /dev/null +++ b/rls/src/test/java/io/grpc/rls/internal/RlsRequestFactoryTest.java @@ -0,0 +1,160 @@ +/* + * Copyright 2020 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.rls.internal; + +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.fail; + +import com.google.common.collect.ImmutableList; +import io.grpc.Metadata; +import io.grpc.Status.Code; +import io.grpc.StatusRuntimeException; +import io.grpc.rls.internal.RlsProtoData.GrpcKeyBuilder; +import io.grpc.rls.internal.RlsProtoData.GrpcKeyBuilder.Name; +import io.grpc.rls.internal.RlsProtoData.NameMatcher; +import io.grpc.rls.internal.RlsProtoData.RequestProcessingStrategy; +import io.grpc.rls.internal.RlsProtoData.RouteLookupConfig; +import io.grpc.rls.internal.RlsProtoData.RouteLookupRequest; +import java.util.concurrent.TimeUnit; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class RlsRequestFactoryTest { + + private static final RouteLookupConfig RLS_CONFIG = + new RouteLookupConfig( + ImmutableList.of( + new GrpcKeyBuilder( + ImmutableList.of(new Name("com.google.service1", "Create")), + ImmutableList.of( + new NameMatcher("user", ImmutableList.of("User", "Parent"), true), + new NameMatcher("id", ImmutableList.of("X-Google-Id"), true))), + new GrpcKeyBuilder( + ImmutableList.of(new Name("com.google.service1")), + ImmutableList.of( + new NameMatcher("user", ImmutableList.of("User", "Parent"), true), + new NameMatcher("password", ImmutableList.of("Password"), true))), + new GrpcKeyBuilder( + ImmutableList.of(new Name("com.google.service2")), + ImmutableList.of( + new NameMatcher("user", ImmutableList.of("User", "Parent"), false), + new NameMatcher("password", ImmutableList.of("Password"), true))), + new GrpcKeyBuilder( + ImmutableList.of(new Name("com.google.service3")), + ImmutableList.of( + new NameMatcher("user", ImmutableList.of("User", "Parent"), true)))), + /* lookupService= */ "foo.google.com", + /* lookupServiceTimeoutInMillis= */ TimeUnit.SECONDS.toMillis(2), + /* maxAgeInMillis= */ TimeUnit.SECONDS.toMillis(300), + /* staleAgeInMillis= */ TimeUnit.SECONDS.toMillis(240), + /* cacheSize= */ 1000, + /* validTargets= */ ImmutableList.of("a valid target"), + /* defaultTarget= */ "us_east_1.cloudbigtable.googleapis.com", + RequestProcessingStrategy.ASYNC_LOOKUP_DEFAULT_TARGET_ON_MISS); + + private final RlsRequestFactory factory = new RlsRequestFactory(RLS_CONFIG); + + @Test + public void create_pathMatches() { + Metadata metadata = new Metadata(); + metadata.put(Metadata.Key.of("User", Metadata.ASCII_STRING_MARSHALLER), "test"); + metadata.put(Metadata.Key.of("X-Google-Id", Metadata.ASCII_STRING_MARSHALLER), "123"); + metadata.put(Metadata.Key.of("foo", Metadata.ASCII_STRING_MARSHALLER), "bar"); + + RouteLookupRequest request = factory.create("com.google.service1", "Create", metadata); + assertThat(request.getTargetType()).isEqualTo("grpc"); + assertThat(request.getPath()).isEqualTo("com.google.service1/Create"); + assertThat(request.getServer()).isEqualTo("foo.google.com"); + assertThat(request.getKeyMap()).containsExactly("user", "test", "id", "123"); + } + + @Test + public void create_missingRequiredHeader() { + Metadata metadata = new Metadata(); + + try { + RouteLookupRequest unused = factory.create("com.google.service2", "Create", metadata); + fail(); + } catch (StatusRuntimeException e) { + assertThat(e.getStatus().getCode()).isEqualTo(Code.INVALID_ARGUMENT); + assertThat(e.getStatus().getDescription()) + .isEqualTo("Missing mandatory metadata(user) not found"); + } + } + + @Test + public void create_pathFallbackMatches() { + Metadata metadata = new Metadata(); + metadata.put(Metadata.Key.of("Parent", Metadata.ASCII_STRING_MARSHALLER), "test"); + metadata.put(Metadata.Key.of("Password", Metadata.ASCII_STRING_MARSHALLER), "hunter2"); + metadata.put(Metadata.Key.of("foo", Metadata.ASCII_STRING_MARSHALLER), "bar"); + + RouteLookupRequest request = factory.create("com.google.service1" , "Update", metadata); + + assertThat(request.getTargetType()).isEqualTo("grpc"); + assertThat(request.getPath()).isEqualTo("com.google.service1/Update"); + assertThat(request.getServer()).isEqualTo("foo.google.com"); + assertThat(request.getKeyMap()).containsExactly("user", "test", "password", "hunter2"); + } + + @Test + public void create_pathFallbackMatches_optionalHeaderMissing() { + Metadata metadata = new Metadata(); + metadata.put(Metadata.Key.of("User", Metadata.ASCII_STRING_MARSHALLER), "test"); + metadata.put(Metadata.Key.of("X-Google-Id", Metadata.ASCII_STRING_MARSHALLER), "123"); + metadata.put(Metadata.Key.of("foo", Metadata.ASCII_STRING_MARSHALLER), "bar"); + + RouteLookupRequest request = factory.create("com.google.service1", "Update", metadata); + + assertThat(request.getTargetType()).isEqualTo("grpc"); + assertThat(request.getPath()).isEqualTo("com.google.service1/Update"); + assertThat(request.getServer()).isEqualTo("foo.google.com"); + assertThat(request.getKeyMap()).containsExactly("user", "test"); + } + + @Test + public void create_unknownPath() { + Metadata metadata = new Metadata(); + metadata.put(Metadata.Key.of("User", Metadata.ASCII_STRING_MARSHALLER), "test"); + metadata.put(Metadata.Key.of("X-Google-Id", Metadata.ASCII_STRING_MARSHALLER), "123"); + metadata.put(Metadata.Key.of("foo", Metadata.ASCII_STRING_MARSHALLER), "bar"); + + RouteLookupRequest request = factory.create("abc.def.service999", "Update", metadata); + + assertThat(request.getTargetType()).isEqualTo("grpc"); + assertThat(request.getPath()).isEqualTo("abc.def.service999/Update"); + assertThat(request.getServer()).isEqualTo("foo.google.com"); + assertThat(request.getKeyMap()).isEmpty(); + } + + @Test + public void create_noMethodInRlsConfig() { + Metadata metadata = new Metadata(); + metadata.put(Metadata.Key.of("User", Metadata.ASCII_STRING_MARSHALLER), "test"); + metadata.put(Metadata.Key.of("X-Google-Id", Metadata.ASCII_STRING_MARSHALLER), "123"); + metadata.put(Metadata.Key.of("foo", Metadata.ASCII_STRING_MARSHALLER), "bar"); + + RouteLookupRequest request = factory.create("com.google.service3", "Update", metadata); + + assertThat(request.getTargetType()).isEqualTo("grpc"); + assertThat(request.getPath()).isEqualTo("com.google.service3/Update"); + assertThat(request.getServer()).isEqualTo("foo.google.com"); + assertThat(request.getKeyMap()).containsExactly("user", "test"); + } +} From 1a1583de5237c637bbe9f2b9a4edbcde8de1e7ab Mon Sep 17 00:00:00 2001 From: Jihun Cho Date: Sat, 11 Apr 2020 00:20:18 -0700 Subject: [PATCH 011/104] rls: delegating helper for rls child policies (#6904) --- .../rls/internal/ChildLoadBalancerHelper.java | 84 +++++++++++++++++++ .../internal/ChildLoadBalancerHelperTest.java | 71 ++++++++++++++++ 2 files changed, 155 insertions(+) create mode 100644 rls/src/main/java/io/grpc/rls/internal/ChildLoadBalancerHelper.java create mode 100644 rls/src/test/java/io/grpc/rls/internal/ChildLoadBalancerHelperTest.java diff --git a/rls/src/main/java/io/grpc/rls/internal/ChildLoadBalancerHelper.java b/rls/src/main/java/io/grpc/rls/internal/ChildLoadBalancerHelper.java new file mode 100644 index 00000000000..6a909c5906c --- /dev/null +++ b/rls/src/main/java/io/grpc/rls/internal/ChildLoadBalancerHelper.java @@ -0,0 +1,84 @@ +/* + * Copyright 2020 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.rls.internal; + +import static com.google.common.base.Preconditions.checkNotNull; + +import io.grpc.ConnectivityState; +import io.grpc.LoadBalancer.Helper; +import io.grpc.LoadBalancer.SubchannelPicker; +import io.grpc.util.ForwardingLoadBalancerHelper; +import javax.annotation.Nonnull; + +/** + * A delegating {@link Helper} for the child load blanacer. The child load-balancer notifies the + * higher level load-blancer with aggregated status instead of each individual child load-blanacer's + * state. + */ +final class ChildLoadBalancerHelper extends ForwardingLoadBalancerHelper { + + private final String target; + private final Helper rlsHelper; + private final SubchannelStateManager subchannelStateManager; + private final SubchannelPicker picker; + + private ChildLoadBalancerHelper( + String target, + Helper rlsHelper, + SubchannelStateManager subchannelStateManager, + SubchannelPicker picker) { + this.target = checkNotNull(target, "target"); + this.rlsHelper = checkNotNull(rlsHelper, "rlsHelper"); + this.subchannelStateManager = checkNotNull(subchannelStateManager, "subchannelStateManager"); + this.picker = checkNotNull(picker, "picker"); + } + + @Override + protected Helper delegate() { + return rlsHelper; + } + + /** + * Updates balancing state from one or more subchannels tracked in the {@link + * SubchannelStateManager}. The passed picker will be ignored, instead the picker which governs + * many subchannels/pickers will be reported to the parent load-balancer. + */ + @Override + public void updateBalancingState( + @Nonnull ConnectivityState newState, + @Nonnull SubchannelPicker unused) { + subchannelStateManager.updateState(target, newState); + super.updateBalancingState(subchannelStateManager.getAggregatedState(), picker); + } + + static final class ChildLoadBalancerHelperProvider { + private final Helper helper; + private final SubchannelStateManager subchannelStateManager; + private final SubchannelPicker picker; + + ChildLoadBalancerHelperProvider( + Helper helper, SubchannelStateManager subchannelStateManager, SubchannelPicker picker) { + this.helper = checkNotNull(helper, "helper"); + this.subchannelStateManager = checkNotNull(subchannelStateManager, "subchannelStateManager"); + this.picker = checkNotNull(picker, "picker"); + } + + ChildLoadBalancerHelper forTarget(String target) { + return new ChildLoadBalancerHelper(target, helper, subchannelStateManager, picker); + } + } +} diff --git a/rls/src/test/java/io/grpc/rls/internal/ChildLoadBalancerHelperTest.java b/rls/src/test/java/io/grpc/rls/internal/ChildLoadBalancerHelperTest.java new file mode 100644 index 00000000000..cbc0c7d3bb7 --- /dev/null +++ b/rls/src/test/java/io/grpc/rls/internal/ChildLoadBalancerHelperTest.java @@ -0,0 +1,71 @@ +/* + * Copyright 2020 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.rls.internal; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.mock; + +import io.grpc.ConnectivityState; +import io.grpc.LoadBalancer.Helper; +import io.grpc.LoadBalancer.SubchannelPicker; +import io.grpc.rls.internal.ChildLoadBalancerHelper.ChildLoadBalancerHelperProvider; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.mockito.InOrder; +import org.mockito.Mockito; + +@RunWith(JUnit4.class) +public class ChildLoadBalancerHelperTest { + + private final Helper helper = mock(Helper.class); + private final SubchannelStateManager subchannelStateManager = new SubchannelStateManagerImpl(); + private final SubchannelPicker picker = mock(SubchannelPicker.class); + private final ChildLoadBalancerHelperProvider provider = + new ChildLoadBalancerHelperProvider(helper, subchannelStateManager, picker); + + @Test + public void childLoadBalancerHelper_shouldReportsSubchannelState() { + InOrder inOrder = Mockito.inOrder(helper); + String target1 = "foo.com"; + ChildLoadBalancerHelper childLbHelper1 = provider.forTarget(target1); + SubchannelPicker picker1 = mock(SubchannelPicker.class); + String target2 = "bar.com"; + ChildLoadBalancerHelper childLbHelper2 = provider.forTarget(target2); + SubchannelPicker picker2 = mock(SubchannelPicker.class); + + assertThat(subchannelStateManager.getState(target1)).isNull(); + assertThat(subchannelStateManager.getState(target2)).isNull(); + + childLbHelper1.updateBalancingState(ConnectivityState.TRANSIENT_FAILURE, picker1); + inOrder.verify(helper).updateBalancingState(ConnectivityState.TRANSIENT_FAILURE, picker); + assertThat(subchannelStateManager.getState(target1)) + .isEqualTo(ConnectivityState.TRANSIENT_FAILURE); + + childLbHelper2.updateBalancingState(ConnectivityState.CONNECTING, picker2); + inOrder.verify(helper).updateBalancingState(ConnectivityState.CONNECTING, picker); + assertThat(subchannelStateManager.getState(target2)).isEqualTo(ConnectivityState.CONNECTING); + + childLbHelper1.updateBalancingState(ConnectivityState.READY, picker1); + inOrder.verify(helper).updateBalancingState(ConnectivityState.READY, picker); + assertThat(subchannelStateManager.getState(target1)).isEqualTo(ConnectivityState.READY); + + childLbHelper1.updateBalancingState(ConnectivityState.SHUTDOWN, picker1); + inOrder.verify(helper).updateBalancingState(ConnectivityState.CONNECTING, picker); + assertThat(subchannelStateManager.getState(target1)).isNull(); + } +} From 050679bdf32ba36ab7aab05efb1f88c41f78a052 Mon Sep 17 00:00:00 2001 From: Jihun Cho Date: Mon, 13 Apr 2020 10:46:19 -0700 Subject: [PATCH 012/104] rls: child lb resolved address factory (#6886) --- .../ChildLbResolvedAddressFactory.java | 50 ++++++++++++++++ .../ChildLbResolvedAddressFactoryTest.java | 57 +++++++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 rls/src/main/java/io/grpc/rls/internal/ChildLbResolvedAddressFactory.java create mode 100644 rls/src/test/java/io/grpc/rls/internal/ChildLbResolvedAddressFactoryTest.java diff --git a/rls/src/main/java/io/grpc/rls/internal/ChildLbResolvedAddressFactory.java b/rls/src/main/java/io/grpc/rls/internal/ChildLbResolvedAddressFactory.java new file mode 100644 index 00000000000..c2315a8da9f --- /dev/null +++ b/rls/src/main/java/io/grpc/rls/internal/ChildLbResolvedAddressFactory.java @@ -0,0 +1,50 @@ +/* + * Copyright 2020 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.rls.internal; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import io.grpc.Attributes; +import io.grpc.EquivalentAddressGroup; +import io.grpc.LoadBalancer.ResolvedAddresses; +import java.util.Collections; +import java.util.List; + +/** Factory to create {@link io.grpc.LoadBalancer.ResolvedAddresses} from child load-balancers. */ +public final class ChildLbResolvedAddressFactory { + + private final List addresses; + private final Attributes attributes; + + /** Constructor. */ + public ChildLbResolvedAddressFactory( + List addresses, Attributes attributes) { + checkArgument(addresses != null && !addresses.isEmpty(), "Address must be provided"); + this.addresses = Collections.unmodifiableList(addresses); + this.attributes = checkNotNull(attributes, "attributes"); + } + + /** Creates {@link ResolvedAddresses} with given child lb config. */ + public ResolvedAddresses create(Object childLbConfig) { + return ResolvedAddresses.newBuilder() + .setAddresses(addresses) + .setAttributes(attributes) + .setLoadBalancingPolicyConfig(childLbConfig) + .build(); + } +} diff --git a/rls/src/test/java/io/grpc/rls/internal/ChildLbResolvedAddressFactoryTest.java b/rls/src/test/java/io/grpc/rls/internal/ChildLbResolvedAddressFactoryTest.java new file mode 100644 index 00000000000..4dbe714d472 --- /dev/null +++ b/rls/src/test/java/io/grpc/rls/internal/ChildLbResolvedAddressFactoryTest.java @@ -0,0 +1,57 @@ +/* + * Copyright 2020 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.rls.internal; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.mock; + +import io.grpc.Attributes; +import io.grpc.EquivalentAddressGroup; +import io.grpc.LoadBalancer.ResolvedAddresses; +import java.net.SocketAddress; +import java.util.ArrayList; +import java.util.List; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class ChildLbResolvedAddressFactoryTest { + + @Test + public void create() { + List addrs = new ArrayList<>(); + addrs.add(new EquivalentAddressGroup(mock(SocketAddress.class))); + Attributes attr = Attributes.newBuilder().build(); + ChildLbResolvedAddressFactory factory = new ChildLbResolvedAddressFactory(addrs, attr); + Object config1 = new Object(); + + ResolvedAddresses resolvedAddress = factory.create(config1); + + assertThat(resolvedAddress.getAddresses()).isEqualTo(addrs); + assertThat(resolvedAddress.getAttributes()).isEqualTo(attr); + assertThat(resolvedAddress.getLoadBalancingPolicyConfig()).isEqualTo(config1); + + Object config2 = "different object"; + + resolvedAddress = factory.create(config2); + + assertThat(resolvedAddress.getAddresses()).isEqualTo(addrs); + assertThat(resolvedAddress.getAttributes()).isEqualTo(attr); + assertThat(resolvedAddress.getLoadBalancingPolicyConfig()).isEqualTo(config2); + } +} From 2cc46acc550632447b7cfd46cf79cd9dafc2b77f Mon Sep 17 00:00:00 2001 From: ashithasantosh <55257063+ashithasantosh@users.noreply.github.com> Date: Mon, 13 Apr 2020 11:18:50 -0700 Subject: [PATCH 013/104] Change logging level from INFO to FINE. (#6920) --- alts/src/main/java/io/grpc/alts/internal/AltsTsiHandshaker.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alts/src/main/java/io/grpc/alts/internal/AltsTsiHandshaker.java b/alts/src/main/java/io/grpc/alts/internal/AltsTsiHandshaker.java index 21824a068c5..844e1038746 100644 --- a/alts/src/main/java/io/grpc/alts/internal/AltsTsiHandshaker.java +++ b/alts/src/main/java/io/grpc/alts/internal/AltsTsiHandshaker.java @@ -189,7 +189,7 @@ public TsiFrameProtector createFrameProtector(int maxFrameSize, ByteBufAllocator maxFrameSize = Math.min(peerMaxFrameSize, AltsTsiFrameProtector.getMaxFrameSize()); maxFrameSize = Math.max(AltsTsiFrameProtector.getMinFrameSize(), maxFrameSize); } - logger.log(Level.INFO, "Maximum frame size value is " + maxFrameSize); + logger.log(Level.FINE, "Maximum frame size value is {0}.", maxFrameSize); return new AltsTsiFrameProtector(maxFrameSize, new AltsChannelCrypter(key, isClient), alloc); } From 2f07c83fed96c4fa4e636c3c84b17ada1b5e9341 Mon Sep 17 00:00:00 2001 From: sanjaypujare Date: Mon, 13 Apr 2020 17:37:26 -0700 Subject: [PATCH 014/104] xds: eliminate downstreamTlsContext from XdsServerBuilder (#6901) * xds: eliminate downstreamTlsContext from XdsServerBuilder Co-authored-by: Jihun Cho --- .../xds/XdsClientWrapperForServerSds.java | 18 +- .../internal/sds/SdsProtocolNegotiators.java | 32 +-- .../xds/internal/sds/XdsServerBuilder.java | 16 +- .../java/io/grpc/xds/CdsLoadBalancerTest.java | 18 +- .../xds/XdsClientWrapperForServerSdsTest.java | 21 ++ .../io/grpc/xds/XdsSdsClientServerTest.java | 213 ++++++++++++++++++ .../io/grpc/xds/XdsServerBuilderTest.java | 2 +- .../ClientSslContextProviderFactoryTest.java | 9 +- .../sds/CommonTlsContextTestsUtil.java | 98 ++++++++ .../sds/SdsClientFileBasedMetadataTest.java | 6 +- .../grpc/xds/internal/sds/SdsClientTest.java | 11 +- .../SdsClientUdsFileBasedMetadataTest.java | 6 +- .../xds/internal/sds/SdsClientUdsTest.java | 8 +- .../sds/SdsProtocolNegotiatorsTest.java | 40 +++- .../sds/SdsSslContextProviderTest.java | 14 +- .../SecretVolumeSslContextProviderTest.java | 108 ++------- .../ServerSslContextProviderFactoryTest.java | 13 +- .../internal/sds/TlsContextManagerTest.java | 31 ++- .../internal/sds/XdsSdsClientServerTest.java | 193 ---------------- .../sds/trust/SdsTrustManagerFactoryTest.java | 20 +- .../sds/trust/SdsX509TrustManagerTest.java | 16 +- 21 files changed, 469 insertions(+), 424 deletions(-) create mode 100644 xds/src/test/java/io/grpc/xds/XdsSdsClientServerTest.java delete mode 100644 xds/src/test/java/io/grpc/xds/internal/sds/XdsSdsClientServerTest.java diff --git a/xds/src/main/java/io/grpc/xds/XdsClientWrapperForServerSds.java b/xds/src/main/java/io/grpc/xds/XdsClientWrapperForServerSds.java index 708268333f3..087127d5cfb 100644 --- a/xds/src/main/java/io/grpc/xds/XdsClientWrapperForServerSds.java +++ b/xds/src/main/java/io/grpc/xds/XdsClientWrapperForServerSds.java @@ -70,6 +70,7 @@ public final class XdsClientWrapperForServerSds { @Nullable private final XdsClient xdsClient; private final int port; private final ScheduledExecutorService timeService; + private final XdsClient.ListenerWatcher listenerWatcher; /** * Factory method for creating a {@link XdsClientWrapperForServerSds}. @@ -106,15 +107,14 @@ public static XdsClientWrapperForServerSds newInstance( this.port = port; this.xdsClient = xdsClient; this.timeService = timeService; - xdsClient.watchListenerData( - port, + this.listenerWatcher = new XdsClient.ListenerWatcher() { @Override public void onListenerChanged(XdsClient.ListenerUpdate update) { logger.log( Level.INFO, - "Setting myListener from ConfigUpdate listener :{0}", - update.getListener().toString()); + "Setting myListener from ConfigUpdate listener: {0}", + update.getListener()); curListener = update.getListener(); } @@ -126,9 +126,10 @@ public void onError(Status error) { curListener = null; } // TODO(sanjaypujare): Implement logic for other cases based on final design. - logger.log(Level.SEVERE, "ListenerWatcher in XdsClientWrapperForServerSds:{0}", error); + logger.log(Level.SEVERE, "ListenerWatcher in XdsClientWrapperForServerSds: {0}", error); } - }); + }; + xdsClient.watchListenerData(port, listenerWatcher); } /** @@ -157,6 +158,11 @@ public DownstreamTlsContext getDownstreamTlsContext(Channel channel) { return null; } + @VisibleForTesting + XdsClient.ListenerWatcher getListenerWatcher() { + return listenerWatcher; + } + private static final class FilterChainComparator implements Comparator { private final InetSocketAddress localAddress; diff --git a/xds/src/main/java/io/grpc/xds/internal/sds/SdsProtocolNegotiators.java b/xds/src/main/java/io/grpc/xds/internal/sds/SdsProtocolNegotiators.java index 8270d2dad11..c50fec650a2 100644 --- a/xds/src/main/java/io/grpc/xds/internal/sds/SdsProtocolNegotiators.java +++ b/xds/src/main/java/io/grpc/xds/internal/sds/SdsProtocolNegotiators.java @@ -16,7 +16,6 @@ package io.grpc.xds.internal.sds; -import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.VisibleForTesting; @@ -74,22 +73,19 @@ public static ProtocolNegotiatorFactory clientProtocolNegotiatorFactory( /** * Creates an SDS based {@link ProtocolNegotiator} for a {@link io.grpc.netty.NettyServerBuilder}. - * Passing {@code null} for downstreamTlsContext will fall back to plaintext. * If xDS returns no DownstreamTlsContext, it will fall back to plaintext. * - * @param downstreamTlsContext passed in {@link XdsServerBuilder#tlsContext}. * @param port the listening port passed to {@link XdsServerBuilder#forPort(int)}. */ public static ProtocolNegotiator serverProtocolNegotiator( - @Nullable DownstreamTlsContext downstreamTlsContext, int port, - SynchronizationContext syncContext) { + int port, SynchronizationContext syncContext) { XdsClientWrapperForServerSds xdsClientWrapperForServerSds = ServerSdsProtocolNegotiator.getXdsClientWrapperForServerSds(port, syncContext); - if (xdsClientWrapperForServerSds == null && downstreamTlsContext == null) { + if (xdsClientWrapperForServerSds == null) { logger.log(Level.INFO, "Fallback to plaintext for server at port {0}", port); return InternalProtocolNegotiators.serverPlaintext(); } else { - return new ServerSdsProtocolNegotiator(downstreamTlsContext, xdsClientWrapperForServerSds); + return new ServerSdsProtocolNegotiator(xdsClientWrapperForServerSds); } } @@ -267,18 +263,13 @@ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) @VisibleForTesting public static final class ServerSdsProtocolNegotiator implements ProtocolNegotiator { - @Nullable private final DownstreamTlsContext downstreamTlsContext; - @Nullable private final XdsClientWrapperForServerSds xdsClientWrapperForServerSds; + private final XdsClientWrapperForServerSds xdsClientWrapperForServerSds; /** Constructor. */ @VisibleForTesting - public ServerSdsProtocolNegotiator( - @Nullable DownstreamTlsContext downstreamTlsContext, - @Nullable XdsClientWrapperForServerSds xdsClientWrapperForServerSds) { - checkArgument(downstreamTlsContext != null || xdsClientWrapperForServerSds != null, - "both downstreamTlsContext and xdsClientWrapperForServerSds cannot be null"); - this.downstreamTlsContext = downstreamTlsContext; - this.xdsClientWrapperForServerSds = xdsClientWrapperForServerSds; + public ServerSdsProtocolNegotiator(XdsClientWrapperForServerSds xdsClientWrapperForServerSds) { + this.xdsClientWrapperForServerSds = + checkNotNull(xdsClientWrapperForServerSds, "xdsClientWrapperForServerSds"); } private static XdsClientWrapperForServerSds getXdsClientWrapperForServerSds( @@ -299,8 +290,7 @@ public AsciiString scheme() { @Override public ChannelHandler newHandler(GrpcHttp2ConnectionHandler grpcHandler) { - return new HandlerPickerHandler(grpcHandler, downstreamTlsContext, - xdsClientWrapperForServerSds); + return new HandlerPickerHandler(grpcHandler, xdsClientWrapperForServerSds); } @Override @@ -315,16 +305,13 @@ public void close() { static final class HandlerPickerHandler extends ChannelInboundHandlerAdapter { private final GrpcHttp2ConnectionHandler grpcHandler; - private final DownstreamTlsContext downstreamTlsContextFromBuilder; private final XdsClientWrapperForServerSds xdsClientWrapperForServerSds; HandlerPickerHandler( GrpcHttp2ConnectionHandler grpcHandler, - @Nullable DownstreamTlsContext downstreamTlsContext, @Nullable XdsClientWrapperForServerSds xdsClientWrapperForServerSds) { checkNotNull(grpcHandler, "grpcHandler"); this.grpcHandler = grpcHandler; - this.downstreamTlsContextFromBuilder = downstreamTlsContext; this.xdsClientWrapperForServerSds = xdsClientWrapperForServerSds; } @@ -339,9 +326,6 @@ public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exc xdsClientWrapperForServerSds == null ? null : xdsClientWrapperForServerSds.getDownstreamTlsContext(ctx.channel()); - if (isTlsContextEmpty(downstreamTlsContext)) { - downstreamTlsContext = downstreamTlsContextFromBuilder; - } if (isTlsContextEmpty(downstreamTlsContext)) { logger.log(Level.INFO, "Fallback to plaintext for {0}", ctx.channel().localAddress()); ctx.pipeline() diff --git a/xds/src/main/java/io/grpc/xds/internal/sds/XdsServerBuilder.java b/xds/src/main/java/io/grpc/xds/internal/sds/XdsServerBuilder.java index 6950f1f98cd..25d802c92fa 100644 --- a/xds/src/main/java/io/grpc/xds/internal/sds/XdsServerBuilder.java +++ b/xds/src/main/java/io/grpc/xds/internal/sds/XdsServerBuilder.java @@ -17,7 +17,6 @@ package io.grpc.xds.internal.sds; import com.google.common.annotations.VisibleForTesting; -import io.envoyproxy.envoy.api.v2.auth.DownstreamTlsContext; import io.grpc.BindableService; import io.grpc.CompressorRegistry; import io.grpc.DecompressorRegistry; @@ -51,9 +50,6 @@ public final class XdsServerBuilder extends ServerBuilder { private final NettyServerBuilder delegate; private final int port; - // TODO (sanjaypujare) integrate with xDS client to get downstreamTlsContext from LDS - @Nullable private DownstreamTlsContext downstreamTlsContext; - private XdsServerBuilder(NettyServerBuilder nettyDelegate, int port) { this.delegate = nettyDelegate; this.port = port; @@ -130,15 +126,6 @@ public XdsServerBuilder intercept(ServerInterceptor interceptor) { return this; } - /** - * Set the DownstreamTlsContext for the server. This is a temporary workaround until integration - * with xDS client is implemented to get LDS. Passing {@code null} will fall back to plaintext. - */ - public XdsServerBuilder tlsContext(@Nullable DownstreamTlsContext downstreamTlsContext) { - this.downstreamTlsContext = downstreamTlsContext; - return this; - } - /** Creates a gRPC server builder for the given port. */ public static XdsServerBuilder forPort(int port) { NettyServerBuilder nettyDelegate = NettyServerBuilder.forAddress(new InetSocketAddress(port)); @@ -173,8 +160,7 @@ void panic(final Throwable t) { } }); InternalProtocolNegotiator.ProtocolNegotiator serverProtocolNegotiator = - SdsProtocolNegotiators.serverProtocolNegotiator( - this.downstreamTlsContext, port, syncContext); + SdsProtocolNegotiators.serverProtocolNegotiator(port, syncContext); return buildServer(serverProtocolNegotiator); } diff --git a/xds/src/test/java/io/grpc/xds/CdsLoadBalancerTest.java b/xds/src/test/java/io/grpc/xds/CdsLoadBalancerTest.java index c6c2e7e342a..7faa0154e0e 100644 --- a/xds/src/test/java/io/grpc/xds/CdsLoadBalancerTest.java +++ b/xds/src/test/java/io/grpc/xds/CdsLoadBalancerTest.java @@ -20,6 +20,11 @@ import static io.grpc.ConnectivityState.CONNECTING; import static io.grpc.ConnectivityState.TRANSIENT_FAILURE; import static io.grpc.xds.XdsLbPolicies.EDS_POLICY_NAME; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.BAD_CLIENT_KEY_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.BAD_CLIENT_PEM_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.CA_PEM_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.CLIENT_KEY_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.CLIENT_PEM_FILE; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.same; @@ -56,7 +61,7 @@ import io.grpc.xds.XdsClient.RefCountedXdsClientObjectPool; import io.grpc.xds.XdsClient.XdsClientFactory; import io.grpc.xds.XdsLoadBalancerProvider.XdsConfig; -import io.grpc.xds.internal.sds.SecretVolumeSslContextProviderTest; +import io.grpc.xds.internal.sds.CommonTlsContextTestsUtil; import io.grpc.xds.internal.sds.SslContextProvider; import io.grpc.xds.internal.sds.TlsContextManager; import java.net.InetSocketAddress; @@ -79,11 +84,6 @@ */ @RunWith(JUnit4.class) public class CdsLoadBalancerTest { - private static final String CLIENT_PEM_FILE = "client.pem"; - private static final String CLIENT_KEY_FILE = "client.key"; - private static final String BADCLIENT_PEM_FILE = "badclient.pem"; - private static final String BADCLIENT_KEY_FILE = "badclient.key"; - private static final String CA_PEM_FILE = "ca.pem"; private final RefCountedXdsClientObjectPool xdsClientPool = new RefCountedXdsClientObjectPool( new XdsClientFactory() { @@ -356,7 +356,7 @@ public void handleCdsConfigs_withUpstreamTlsContext() { verify(xdsClient).watchClusterData(eq("foo.googleapis.com"), clusterWatcherCaptor1.capture()); UpstreamTlsContext upstreamTlsContext = - SecretVolumeSslContextProviderTest.buildUpstreamTlsContextFromFilenames( + CommonTlsContextTestsUtil.buildUpstreamTlsContextFromFilenames( CLIENT_KEY_FILE, CLIENT_PEM_FILE, CA_PEM_FILE); SslContextProvider mockSslContextProvider = @@ -415,8 +415,8 @@ public void handleCdsConfigs_withUpstreamTlsContext() { reset(mockTlsContextManager); reset(helper); UpstreamTlsContext upstreamTlsContext1 = - SecretVolumeSslContextProviderTest.buildUpstreamTlsContextFromFilenames( - BADCLIENT_KEY_FILE, BADCLIENT_PEM_FILE, CA_PEM_FILE); + CommonTlsContextTestsUtil.buildUpstreamTlsContextFromFilenames( + BAD_CLIENT_KEY_FILE, BAD_CLIENT_PEM_FILE, CA_PEM_FILE); SslContextProvider mockSslContextProvider1 = (SslContextProvider) mock(SslContextProvider.class); doReturn(upstreamTlsContext1).when(mockSslContextProvider1).getSource(); diff --git a/xds/src/test/java/io/grpc/xds/XdsClientWrapperForServerSdsTest.java b/xds/src/test/java/io/grpc/xds/XdsClientWrapperForServerSdsTest.java index f6d6f8304e0..e0882bf94cf 100644 --- a/xds/src/test/java/io/grpc/xds/XdsClientWrapperForServerSdsTest.java +++ b/xds/src/test/java/io/grpc/xds/XdsClientWrapperForServerSdsTest.java @@ -18,6 +18,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -139,6 +140,26 @@ public static Iterable data() { private XdsClientWrapperForServerSds xdsClientWrapperForServerSds; private final DownstreamTlsContext[] tlsContexts = new DownstreamTlsContext[3]; + /** Creates XdsClientWrapperForServerSds: also used by other classes. */ + public static XdsClientWrapperForServerSds createXdsClientWrapperForServerSds( + int port, DownstreamTlsContext downstreamTlsContext) { + XdsClient mockXdsClient = mock(XdsClient.class); + XdsClientWrapperForServerSds xdsClientWrapperForServerSds = + new XdsClientWrapperForServerSds(port, mockXdsClient, null); + generateListenerUpdateToWatcher( + port, downstreamTlsContext, xdsClientWrapperForServerSds.getListenerWatcher()); + return xdsClientWrapperForServerSds; + } + + static void generateListenerUpdateToWatcher( + int port, DownstreamTlsContext tlsContext, XdsClient.ListenerWatcher registeredWatcher) { + EnvoyServerProtoData.Listener listener = + XdsSdsClientServerTest.buildListener("listener1", "0.0.0.0", port, tlsContext); + XdsClient.ListenerUpdate listenerUpdate = + XdsClient.ListenerUpdate.newBuilder().setListener(listener).build(); + registeredWatcher.onListenerChanged(listenerUpdate); + } + @Before public void setUp() throws IOException { MockitoAnnotations.initMocks(this); diff --git a/xds/src/test/java/io/grpc/xds/XdsSdsClientServerTest.java b/xds/src/test/java/io/grpc/xds/XdsSdsClientServerTest.java new file mode 100644 index 00000000000..8d1e09ff463 --- /dev/null +++ b/xds/src/test/java/io/grpc/xds/XdsSdsClientServerTest.java @@ -0,0 +1,213 @@ +/* + * Copyright 2019 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; + +import static com.google.common.truth.Truth.assertThat; +import static io.grpc.xds.XdsClientWrapperForServerSdsTest.buildFilterChainMatch; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.BAD_SERVER_KEY_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.BAD_SERVER_PEM_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.CA_PEM_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.CLIENT_KEY_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.CLIENT_PEM_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.SERVER_1_KEY_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.SERVER_1_PEM_FILE; +import static org.junit.Assert.fail; + +import io.envoyproxy.envoy.api.v2.auth.DownstreamTlsContext; +import io.envoyproxy.envoy.api.v2.auth.UpstreamTlsContext; +import io.grpc.Server; +import io.grpc.StatusRuntimeException; +import io.grpc.stub.StreamObserver; +import io.grpc.testing.GrpcCleanupRule; +import io.grpc.testing.protobuf.SimpleRequest; +import io.grpc.testing.protobuf.SimpleResponse; +import io.grpc.testing.protobuf.SimpleServiceGrpc; +import io.grpc.xds.internal.sds.CommonTlsContextTestsUtil; +import io.grpc.xds.internal.sds.SdsProtocolNegotiators; +import io.grpc.xds.internal.sds.XdsChannelBuilder; +import io.grpc.xds.internal.sds.XdsServerBuilder; +import java.io.IOException; +import java.net.ServerSocket; +import java.util.Arrays; +import javax.net.ssl.SSLHandshakeException; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** + * Unit tests for {@link XdsChannelBuilder} and {@link XdsServerBuilder} for plaintext/TLS/mTLS + * modes. + */ +@RunWith(JUnit4.class) +public class XdsSdsClientServerTest { + + @Rule public final GrpcCleanupRule cleanupRule = new GrpcCleanupRule(); + private int port; + + @Before + public void setUp() throws IOException { + port = findFreePort(); + } + + @Test + public void plaintextClientServer() throws IOException { + Server unused = buildServerWithTlsContext(/* downstreamTlsContext= */ null); + + SimpleServiceGrpc.SimpleServiceBlockingStub blockingStub = + getBlockingStub(/* upstreamTlsContext= */ null, /* overrideAuthority= */ null); + assertThat(unaryRpc("buddy", blockingStub)).isEqualTo("Hello buddy"); + } + + /** TLS channel - no mTLS. */ + @Test + public void tlsClientServer_noClientAuthentication() throws IOException { + DownstreamTlsContext downstreamTlsContext = + CommonTlsContextTestsUtil.buildDownstreamTlsContextFromFilenames( + SERVER_1_KEY_FILE, SERVER_1_PEM_FILE, null); + Server unused = buildServerWithTlsContext(downstreamTlsContext); + + // for TLS, client only needs trustCa + UpstreamTlsContext upstreamTlsContext = + CommonTlsContextTestsUtil.buildUpstreamTlsContextFromFilenames( + /* privateKey= */ null, /* certChain= */ null, CA_PEM_FILE); + + SimpleServiceGrpc.SimpleServiceBlockingStub blockingStub = + getBlockingStub(upstreamTlsContext, /* overrideAuthority= */ "foo.test.google.fr"); + assertThat(unaryRpc(/* requestMessage= */ "buddy", blockingStub)).isEqualTo("Hello buddy"); + } + + /** mTLS - client auth enabled. */ + @Test + public void mtlsClientServer_withClientAuthentication() throws IOException { + UpstreamTlsContext upstreamTlsContext = + CommonTlsContextTestsUtil.buildUpstreamTlsContextFromFilenames( + CLIENT_KEY_FILE, CLIENT_PEM_FILE, CA_PEM_FILE); + XdsClient.ListenerWatcher unused = performMtlsTestAndGetListenerWatcher(upstreamTlsContext); + } + + /** mTLS - client auth enabled then update server certs to untrusted. */ + @Test + public void mtlsClientServer_changeServerContext_expectException() throws IOException { + UpstreamTlsContext upstreamTlsContext = + CommonTlsContextTestsUtil.buildUpstreamTlsContextFromFilenames( + CLIENT_KEY_FILE, CLIENT_PEM_FILE, CA_PEM_FILE); + XdsClient.ListenerWatcher listenerWatcher = + performMtlsTestAndGetListenerWatcher(upstreamTlsContext); + DownstreamTlsContext downstreamTlsContext = + CommonTlsContextTestsUtil.buildDownstreamTlsContextFromFilenames( + BAD_SERVER_KEY_FILE, BAD_SERVER_PEM_FILE, CA_PEM_FILE); + XdsClientWrapperForServerSdsTest.generateListenerUpdateToWatcher( + port, downstreamTlsContext, listenerWatcher); + try { + SimpleServiceGrpc.SimpleServiceBlockingStub blockingStub = + getBlockingStub(upstreamTlsContext, "foo.test.google.fr"); + assertThat(unaryRpc("buddy", blockingStub)).isEqualTo("Hello buddy"); + fail("exception expected"); + } catch (StatusRuntimeException sre) { + assertThat(sre).hasCauseThat().isInstanceOf(SSLHandshakeException.class); + assertThat(sre).hasCauseThat().hasMessageThat().isEqualTo("General OpenSslEngine problem"); + } + } + + private XdsClient.ListenerWatcher performMtlsTestAndGetListenerWatcher( + UpstreamTlsContext upstreamTlsContext) throws IOException { + DownstreamTlsContext downstreamTlsContext = + CommonTlsContextTestsUtil.buildDownstreamTlsContextFromFilenames( + SERVER_1_KEY_FILE, SERVER_1_PEM_FILE, CA_PEM_FILE); + + final XdsClientWrapperForServerSds xdsClientWrapperForServerSds = + XdsClientWrapperForServerSdsTest.createXdsClientWrapperForServerSds( + port, /* downstreamTlsContext= */ downstreamTlsContext); + SdsProtocolNegotiators.ServerSdsProtocolNegotiator serverSdsProtocolNegotiator = + new SdsProtocolNegotiators.ServerSdsProtocolNegotiator(xdsClientWrapperForServerSds); + Server unused = getServer(port, serverSdsProtocolNegotiator); + + XdsClient.ListenerWatcher listenerWatcher = xdsClientWrapperForServerSds.getListenerWatcher(); + + SimpleServiceGrpc.SimpleServiceBlockingStub blockingStub = + getBlockingStub(upstreamTlsContext, "foo.test.google.fr"); + assertThat(unaryRpc("buddy", blockingStub)).isEqualTo("Hello buddy"); + return listenerWatcher; + } + + private Server buildServerWithTlsContext(DownstreamTlsContext downstreamTlsContext) + throws IOException { + final XdsClientWrapperForServerSds xdsClientWrapperForServerSds = + XdsClientWrapperForServerSdsTest.createXdsClientWrapperForServerSds( + port, /* downstreamTlsContext= */ downstreamTlsContext); + SdsProtocolNegotiators.ServerSdsProtocolNegotiator serverSdsProtocolNegotiator = + new SdsProtocolNegotiators.ServerSdsProtocolNegotiator(xdsClientWrapperForServerSds); + return getServer(port, serverSdsProtocolNegotiator); + } + + private Server getServer( + int port, SdsProtocolNegotiators.ServerSdsProtocolNegotiator serverSdsProtocolNegotiator) + throws IOException { + XdsServerBuilder builder = XdsServerBuilder.forPort(port).addService(new SimpleServiceImpl()); + return cleanupRule.register(builder.buildServer(serverSdsProtocolNegotiator)).start(); + } + + private static int findFreePort() throws IOException { + try (ServerSocket socket = new ServerSocket(0)) { + socket.setReuseAddress(true); + return socket.getLocalPort(); + } + } + + static EnvoyServerProtoData.Listener buildListener( + String name, String address, int port, DownstreamTlsContext tlsContext) { + EnvoyServerProtoData.FilterChainMatch filterChainMatch = buildFilterChainMatch(port, address); + EnvoyServerProtoData.FilterChain filterChain1 = + new EnvoyServerProtoData.FilterChain(filterChainMatch, tlsContext); + EnvoyServerProtoData.Listener listener = + new EnvoyServerProtoData.Listener(name, address, Arrays.asList(filterChain1)); + return listener; + } + + private SimpleServiceGrpc.SimpleServiceBlockingStub getBlockingStub( + UpstreamTlsContext upstreamTlsContext, String overrideAuthority) { + XdsChannelBuilder builder = + XdsChannelBuilder.forTarget("localhost:" + port).tlsContext(upstreamTlsContext); + if (overrideAuthority != null) { + builder = builder.overrideAuthority(overrideAuthority); + } + return SimpleServiceGrpc.newBlockingStub(cleanupRule.register(builder.build())); + } + + /** Say hello to server. */ + private static String unaryRpc( + String requestMessage, SimpleServiceGrpc.SimpleServiceBlockingStub blockingStub) { + SimpleRequest request = SimpleRequest.newBuilder().setRequestMessage(requestMessage).build(); + SimpleResponse response = blockingStub.unaryRpc(request); + return response.getResponseMessage(); + } + + private static class SimpleServiceImpl extends SimpleServiceGrpc.SimpleServiceImplBase { + + @Override + public void unaryRpc(SimpleRequest req, StreamObserver responseObserver) { + SimpleResponse response = + SimpleResponse.newBuilder() + .setResponseMessage("Hello " + req.getRequestMessage()) + .build(); + responseObserver.onNext(response); + responseObserver.onCompleted(); + } + } +} diff --git a/xds/src/test/java/io/grpc/xds/XdsServerBuilderTest.java b/xds/src/test/java/io/grpc/xds/XdsServerBuilderTest.java index 5562347a978..d2546b1d234 100644 --- a/xds/src/test/java/io/grpc/xds/XdsServerBuilderTest.java +++ b/xds/src/test/java/io/grpc/xds/XdsServerBuilderTest.java @@ -44,7 +44,7 @@ public void xdsServer_callsShutdown() throws IOException, InterruptedException { XdsClientWrapperForServerSds xdsClientWrapperForServerSds = new XdsClientWrapperForServerSds(port, mockXdsClient, null); ServerSdsProtocolNegotiator serverSdsProtocolNegotiator = - new ServerSdsProtocolNegotiator(null, xdsClientWrapperForServerSds); + new ServerSdsProtocolNegotiator(xdsClientWrapperForServerSds); Server xdsServer = builder.buildServer(serverSdsProtocolNegotiator); xdsServer.start(); xdsServer.shutdown(); diff --git a/xds/src/test/java/io/grpc/xds/internal/sds/ClientSslContextProviderFactoryTest.java b/xds/src/test/java/io/grpc/xds/internal/sds/ClientSslContextProviderFactoryTest.java index a72b70931c2..be869505608 100644 --- a/xds/src/test/java/io/grpc/xds/internal/sds/ClientSslContextProviderFactoryTest.java +++ b/xds/src/test/java/io/grpc/xds/internal/sds/ClientSslContextProviderFactoryTest.java @@ -17,6 +17,9 @@ package io.grpc.xds.internal.sds; import static com.google.common.truth.Truth.assertThat; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.CA_PEM_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.CLIENT_KEY_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.CLIENT_PEM_FILE; import io.envoyproxy.envoy.api.v2.auth.CommonTlsContext; import io.envoyproxy.envoy.api.v2.auth.UpstreamTlsContext; @@ -29,17 +32,13 @@ @RunWith(JUnit4.class) public class ClientSslContextProviderFactoryTest { - private static final String CLIENT_PEM_FILE = "client.pem"; - private static final String CLIENT_KEY_FILE = "client.key"; - private static final String CA_PEM_FILE = "ca.pem"; - ClientSslContextProviderFactory clientSslContextProviderFactory = new ClientSslContextProviderFactory(); @Test public void createSslContextProvider_allFilenames() { UpstreamTlsContext upstreamTlsContext = - SecretVolumeSslContextProviderTest.buildUpstreamTlsContextFromFilenames( + CommonTlsContextTestsUtil.buildUpstreamTlsContextFromFilenames( CLIENT_KEY_FILE, CLIENT_PEM_FILE, CA_PEM_FILE); SslContextProvider sslContextProvider = diff --git a/xds/src/test/java/io/grpc/xds/internal/sds/CommonTlsContextTestsUtil.java b/xds/src/test/java/io/grpc/xds/internal/sds/CommonTlsContextTestsUtil.java index a7dc2652980..0f1bb235d3e 100644 --- a/xds/src/test/java/io/grpc/xds/internal/sds/CommonTlsContextTestsUtil.java +++ b/xds/src/test/java/io/grpc/xds/internal/sds/CommonTlsContextTestsUtil.java @@ -23,12 +23,29 @@ import io.envoyproxy.envoy.api.v2.auth.DownstreamTlsContext; import io.envoyproxy.envoy.api.v2.auth.SdsSecretConfig; import io.envoyproxy.envoy.api.v2.auth.TlsCertificate; +import io.envoyproxy.envoy.api.v2.auth.UpstreamTlsContext; import io.envoyproxy.envoy.api.v2.core.DataSource; +import io.grpc.internal.testing.TestUtils; +import java.io.IOException; import java.util.Arrays; +import javax.annotation.Nullable; /** Utility class for client and server ssl provider tests. */ public class CommonTlsContextTestsUtil { + public static final String SERVER_0_PEM_FILE = "server0.pem"; + public static final String SERVER_0_KEY_FILE = "server0.key"; + public static final String SERVER_1_PEM_FILE = "server1.pem"; + public static final String SERVER_1_KEY_FILE = "server1.key"; + public static final String CLIENT_PEM_FILE = "client.pem"; + public static final String CLIENT_KEY_FILE = "client.key"; + public static final String CA_PEM_FILE = "ca.pem"; + /** Bad/untrusted server certs. */ + public static final String BAD_SERVER_PEM_FILE = "badserver.pem"; + public static final String BAD_SERVER_KEY_FILE = "badserver.key"; + public static final String BAD_CLIENT_PEM_FILE = "badclient.pem"; + public static final String BAD_CLIENT_KEY_FILE = "badclient.key"; + static SdsSecretConfig buildSdsSecretConfig(String name, String targetUri, String channelType) { SdsSecretConfig sdsSecretConfig = null; if (!Strings.isNullOrEmpty(name) && !Strings.isNullOrEmpty(targetUri)) { @@ -144,4 +161,85 @@ public static DownstreamTlsContext buildTestDownstreamTlsContext( Arrays.asList("managed-tls"), null)); } + + static String getTempFileNameForResourcesFile(String resFile) throws IOException { + return TestUtils.loadCert(resFile).getAbsolutePath(); + } + + /** + * Helper method to build DownstreamTlsContext for above tests. Called from other classes as well. + */ + public static DownstreamTlsContext buildDownstreamTlsContextFromFilenames( + @Nullable String privateKey, @Nullable String certChain, @Nullable String trustCa) { + // get temp file for each file + try { + if (certChain != null) { + certChain = getTempFileNameForResourcesFile(certChain); + } + if (privateKey != null) { + privateKey = getTempFileNameForResourcesFile(privateKey); + } + if (trustCa != null) { + trustCa = getTempFileNameForResourcesFile(trustCa); + } + } catch (IOException ioe) { + throw new RuntimeException(ioe); + } + return buildDownstreamTlsContext( + buildCommonTlsContextFromFilenames(privateKey, certChain, trustCa)); + } + + /** + * Helper method to build UpstreamTlsContext for above tests. Called from other classes as well. + */ + public static UpstreamTlsContext buildUpstreamTlsContextFromFilenames( + @Nullable String privateKey, @Nullable String certChain, @Nullable String trustCa) { + try { + if (certChain != null) { + certChain = getTempFileNameForResourcesFile(certChain); + } + if (privateKey != null) { + privateKey = getTempFileNameForResourcesFile(privateKey); + } + if (trustCa != null) { + trustCa = getTempFileNameForResourcesFile(trustCa); + } + } catch (IOException ioe) { + throw new RuntimeException(ioe); + } + return SecretVolumeSslContextProviderTest.buildUpstreamTlsContext( + buildCommonTlsContextFromFilenames(privateKey, certChain, trustCa)); + } + + private static CommonTlsContext buildCommonTlsContextFromFilenames( + String privateKey, String certChain, String trustCa) { + TlsCertificate tlsCert = null; + if (!Strings.isNullOrEmpty(privateKey) && !Strings.isNullOrEmpty(certChain)) { + tlsCert = + TlsCertificate.newBuilder() + .setCertificateChain(DataSource.newBuilder().setFilename(certChain)) + .setPrivateKey(DataSource.newBuilder().setFilename(privateKey)) + .build(); + } + CertificateValidationContext certContext = null; + if (!Strings.isNullOrEmpty(trustCa)) { + certContext = + CertificateValidationContext.newBuilder() + .setTrustedCa(DataSource.newBuilder().setFilename(trustCa)) + .build(); + } + return getCommonTlsContext(tlsCert, certContext); + } + + static CommonTlsContext getCommonTlsContext( + TlsCertificate tlsCertificate, CertificateValidationContext certContext) { + CommonTlsContext.Builder builder = CommonTlsContext.newBuilder(); + if (tlsCertificate != null) { + builder = builder.addTlsCertificates(tlsCertificate); + } + if (certContext != null) { + builder = builder.setValidationContext(certContext); + } + return builder.build(); + } } diff --git a/xds/src/test/java/io/grpc/xds/internal/sds/SdsClientFileBasedMetadataTest.java b/xds/src/test/java/io/grpc/xds/internal/sds/SdsClientFileBasedMetadataTest.java index fda58feaaae..6d84b9fca93 100644 --- a/xds/src/test/java/io/grpc/xds/internal/sds/SdsClientFileBasedMetadataTest.java +++ b/xds/src/test/java/io/grpc/xds/internal/sds/SdsClientFileBasedMetadataTest.java @@ -17,6 +17,8 @@ package io.grpc.xds.internal.sds; import static com.google.common.truth.Truth.assertThat; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.SERVER_0_KEY_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.SERVER_0_PEM_FILE; import static org.junit.Assert.fail; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; @@ -217,9 +219,7 @@ public void configSource_badPluginName_expectException() { public void testSecretWatcher_tlsCertificate() throws IOException { SdsClient.SecretWatcher mockWatcher = mock(SdsClient.SecretWatcher.class); - doReturn( - SdsClientTest.getOneTlsCertSecret( - "name1", SdsClientTest.SERVER_0_KEY_FILE, SdsClientTest.SERVER_0_PEM_FILE)) + doReturn(SdsClientTest.getOneTlsCertSecret("name1", SERVER_0_KEY_FILE, SERVER_0_PEM_FILE)) .when(serverMock) .getSecretFor("name1"); diff --git a/xds/src/test/java/io/grpc/xds/internal/sds/SdsClientTest.java b/xds/src/test/java/io/grpc/xds/internal/sds/SdsClientTest.java index 0927e7e286e..da5c0c1bc32 100644 --- a/xds/src/test/java/io/grpc/xds/internal/sds/SdsClientTest.java +++ b/xds/src/test/java/io/grpc/xds/internal/sds/SdsClientTest.java @@ -17,6 +17,11 @@ package io.grpc.xds.internal.sds; import static com.google.common.truth.Truth.assertThat; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.CA_PEM_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.SERVER_0_KEY_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.SERVER_0_PEM_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.SERVER_1_KEY_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.SERVER_1_PEM_FILE; import static org.junit.Assert.fail; import static org.mockito.Mockito.any; import static org.mockito.Mockito.doThrow; @@ -65,12 +70,6 @@ @RunWith(JUnit4.class) public class SdsClientTest { - static final String SERVER_0_PEM_FILE = "server0.pem"; - static final String SERVER_0_KEY_FILE = "server0.key"; - static final String SERVER_1_PEM_FILE = "server1.pem"; - static final String SERVER_1_KEY_FILE = "server1.key"; - static final String CA_PEM_FILE = "ca.pem"; - private TestSdsServer.ServerMock serverMock; private TestSdsServer server; private SdsClient sdsClient; diff --git a/xds/src/test/java/io/grpc/xds/internal/sds/SdsClientUdsFileBasedMetadataTest.java b/xds/src/test/java/io/grpc/xds/internal/sds/SdsClientUdsFileBasedMetadataTest.java index 02fde83dfc3..25fe4cf6fd1 100644 --- a/xds/src/test/java/io/grpc/xds/internal/sds/SdsClientUdsFileBasedMetadataTest.java +++ b/xds/src/test/java/io/grpc/xds/internal/sds/SdsClientUdsFileBasedMetadataTest.java @@ -17,6 +17,8 @@ package io.grpc.xds.internal.sds; import static com.google.common.truth.Truth.assertThat; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.SERVER_0_KEY_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.SERVER_0_PEM_FILE; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; @@ -94,9 +96,7 @@ public void teardown() throws InterruptedException { public void testSecretWatcher_tlsCertificate() throws IOException, InterruptedException { final SdsClient.SecretWatcher mockWatcher = mock(SdsClient.SecretWatcher.class); - doReturn( - SdsClientTest.getOneTlsCertSecret( - "name1", SdsClientTest.SERVER_0_KEY_FILE, SdsClientTest.SERVER_0_PEM_FILE)) + doReturn(SdsClientTest.getOneTlsCertSecret("name1", SERVER_0_KEY_FILE, SERVER_0_PEM_FILE)) .when(serverMock) .getSecretFor("name1"); diff --git a/xds/src/test/java/io/grpc/xds/internal/sds/SdsClientUdsTest.java b/xds/src/test/java/io/grpc/xds/internal/sds/SdsClientUdsTest.java index 80f9ac18f69..9f5431f6a0e 100644 --- a/xds/src/test/java/io/grpc/xds/internal/sds/SdsClientUdsTest.java +++ b/xds/src/test/java/io/grpc/xds/internal/sds/SdsClientUdsTest.java @@ -17,6 +17,10 @@ package io.grpc.xds.internal.sds; import static com.google.common.truth.Truth.assertThat; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.SERVER_0_KEY_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.SERVER_0_PEM_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.SERVER_1_KEY_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.SERVER_1_PEM_FILE; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; @@ -45,10 +49,6 @@ @RunWith(JUnit4.class) public class SdsClientUdsTest { - private static final String SERVER_0_PEM_FILE = "server0.pem"; - private static final String SERVER_0_KEY_FILE = "server0.key"; - private static final String SERVER_1_PEM_FILE = "server1.pem"; - private static final String SERVER_1_KEY_FILE = "server1.key"; private static final String SDSCLIENT_TEST_SOCKET = "/tmp/sdsclient-test.socket"; private TestSdsServer.ServerMock serverMock; diff --git a/xds/src/test/java/io/grpc/xds/internal/sds/SdsProtocolNegotiatorsTest.java b/xds/src/test/java/io/grpc/xds/internal/sds/SdsProtocolNegotiatorsTest.java index ce052357494..b7a9336ccfb 100644 --- a/xds/src/test/java/io/grpc/xds/internal/sds/SdsProtocolNegotiatorsTest.java +++ b/xds/src/test/java/io/grpc/xds/internal/sds/SdsProtocolNegotiatorsTest.java @@ -17,6 +17,11 @@ package io.grpc.xds.internal.sds; import static com.google.common.truth.Truth.assertThat; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.CA_PEM_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.CLIENT_KEY_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.CLIENT_PEM_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.SERVER_1_KEY_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.SERVER_1_PEM_FILE; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @@ -31,6 +36,8 @@ import io.grpc.netty.GrpcHttp2ConnectionHandler; import io.grpc.netty.InternalProtocolNegotiationEvent; import io.grpc.netty.InternalProtocolNegotiator; +import io.grpc.xds.XdsClientWrapperForServerSds; +import io.grpc.xds.XdsClientWrapperForServerSdsTest; import io.grpc.xds.internal.sds.SdsProtocolNegotiators.ClientSdsHandler; import io.grpc.xds.internal.sds.SdsProtocolNegotiators.ClientSdsProtocolNegotiator; import io.netty.channel.ChannelHandler; @@ -49,6 +56,8 @@ import io.netty.handler.ssl.SslHandler; import io.netty.handler.ssl.SslHandshakeCompletionEvent; import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.SocketAddress; import java.util.Iterator; import java.util.Map; import org.junit.Test; @@ -59,12 +68,6 @@ @RunWith(JUnit4.class) public class SdsProtocolNegotiatorsTest { - private static final String SERVER_1_PEM_FILE = "server1.pem"; - private static final String SERVER_1_KEY_FILE = "server1.key"; - private static final String CLIENT_PEM_FILE = "client.pem"; - private static final String CLIENT_KEY_FILE = "client.key"; - private static final String CA_PEM_FILE = "ca.pem"; - private final GrpcHttp2ConnectionHandler grpcHandler = FakeGrpcHttp2ConnectionHandler.newHandler(); @@ -153,7 +156,8 @@ public void clientSdsProtocolNegotiatorNewHandler_nullTlsContext() { @Test public void clientSdsProtocolNegotiatorNewHandler_nonNullTlsContext() { UpstreamTlsContext upstreamTlsContext = - buildUpstreamTlsContext(getCommonTlsContext(null, null)); + buildUpstreamTlsContext( + getCommonTlsContext(/* tlsCertificate= */ null, /* certContext= */ null)); ClientSdsProtocolNegotiator pn = new ClientSdsProtocolNegotiator(upstreamTlsContext); ChannelHandler newHandler = pn.newHandler(grpcHandler); assertThat(newHandler).isNotNull(); @@ -187,11 +191,23 @@ public void clientSdsHandler_addLast() throws IOException { @Test public void serverSdsHandler_addLast() throws IOException { + // we need InetSocketAddress instead of EmbeddedSocketAddress as localAddress for this test + channel = + new EmbeddedChannel() { + @Override + public SocketAddress localAddress() { + return new InetSocketAddress("172.168.1.1", 80); + } + }; + pipeline = channel.pipeline(); DownstreamTlsContext downstreamTlsContext = buildDownstreamTlsContextFromFilenames(SERVER_1_KEY_FILE, SERVER_1_PEM_FILE, CA_PEM_FILE); + XdsClientWrapperForServerSds xdsClientWrapperForServerSds = + XdsClientWrapperForServerSdsTest.createXdsClientWrapperForServerSds( + 80, downstreamTlsContext); SdsProtocolNegotiators.HandlerPickerHandler handlerPickerHandler = - new SdsProtocolNegotiators.HandlerPickerHandler(grpcHandler, downstreamTlsContext, null); + new SdsProtocolNegotiators.HandlerPickerHandler(grpcHandler, xdsClientWrapperForServerSds); pipeline.addLast(handlerPickerHandler); channelHandlerCtx = pipeline.context(handlerPickerHandler); assertThat(channelHandlerCtx).isNotNull(); // should find HandlerPickerHandler @@ -217,7 +233,8 @@ public void serverSdsHandler_addLast() throws IOException { @Test public void serverSdsHandler_nullTlsContext_expectPlaintext() throws IOException { SdsProtocolNegotiators.HandlerPickerHandler handlerPickerHandler = - new SdsProtocolNegotiators.HandlerPickerHandler(grpcHandler, null, null); + new SdsProtocolNegotiators.HandlerPickerHandler( + grpcHandler, /* xdsClientWrapperForServerSds= */ null); pipeline.addLast(handlerPickerHandler); channelHandlerCtx = pipeline.context(handlerPickerHandler); assertThat(channelHandlerCtx).isNotNull(); // should find HandlerPickerHandler @@ -259,10 +276,9 @@ public void clientSdsProtocolNegotiatorNewHandler_fireProtocolNegotiationEvent() } @Test - public void serverSdsProtocolNegotiator_passNulls_expectPlaintext() { + public void serverSdsProtocolNegotiator_nullSyncContext_expectPlaintext() { InternalProtocolNegotiator.ProtocolNegotiator protocolNegotiator = - SdsProtocolNegotiators.serverProtocolNegotiator(null, 7000, - null); + SdsProtocolNegotiators.serverProtocolNegotiator(/* port= */ 7000, /* syncContext= */ null); assertThat(protocolNegotiator.scheme().toString()).isEqualTo("http"); } diff --git a/xds/src/test/java/io/grpc/xds/internal/sds/SdsSslContextProviderTest.java b/xds/src/test/java/io/grpc/xds/internal/sds/SdsSslContextProviderTest.java index 1a31b6c6b77..db8e13d5250 100644 --- a/xds/src/test/java/io/grpc/xds/internal/sds/SdsSslContextProviderTest.java +++ b/xds/src/test/java/io/grpc/xds/internal/sds/SdsSslContextProviderTest.java @@ -17,6 +17,11 @@ package io.grpc.xds.internal.sds; import static com.google.common.truth.Truth.assertThat; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.CA_PEM_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.CLIENT_KEY_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.CLIENT_PEM_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.SERVER_1_KEY_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.SERVER_1_PEM_FILE; import static io.grpc.xds.internal.sds.SdsClientTest.getOneCertificateValidationContextSecret; import static io.grpc.xds.internal.sds.SdsClientTest.getOneTlsCertSecret; import static io.grpc.xds.internal.sds.SecretVolumeSslContextProviderTest.doChecksOnSslContext; @@ -39,12 +44,6 @@ @RunWith(JUnit4.class) public class SdsSslContextProviderTest { - private static final String SERVER_1_PEM_FILE = "server1.pem"; - private static final String SERVER_1_KEY_FILE = "server1.key"; - private static final String CLIENT_PEM_FILE = "client.pem"; - private static final String CLIENT_KEY_FILE = "client.key"; - private static final String CA_PEM_FILE = "ca.pem"; - private TestSdsServer.ServerMock serverMock; private TestSdsServer server; private Node node; @@ -182,8 +181,7 @@ public void testProviderForClient_withSubjectAltNames() throws IOException { when(serverMock.getSecretFor(/* name= */ "cert1")) .thenReturn(getOneTlsCertSecret(/* name= */ "cert1", CLIENT_KEY_FILE, CLIENT_PEM_FILE)); when(serverMock.getSecretFor("valid1")) - .thenReturn(getOneCertificateValidationContextSecret(/* name= */ "valid1", - CA_PEM_FILE)); + .thenReturn(getOneCertificateValidationContextSecret(/* name= */ "valid1", CA_PEM_FILE)); SdsSslContextProvider provider = getSdsSslContextProvider( diff --git a/xds/src/test/java/io/grpc/xds/internal/sds/SecretVolumeSslContextProviderTest.java b/xds/src/test/java/io/grpc/xds/internal/sds/SecretVolumeSslContextProviderTest.java index 6477188621e..6d5e5bdc832 100644 --- a/xds/src/test/java/io/grpc/xds/internal/sds/SecretVolumeSslContextProviderTest.java +++ b/xds/src/test/java/io/grpc/xds/internal/sds/SecretVolumeSslContextProviderTest.java @@ -17,16 +17,18 @@ package io.grpc.xds.internal.sds; import static com.google.common.truth.Truth.assertThat; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.CA_PEM_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.CLIENT_KEY_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.CLIENT_PEM_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.SERVER_1_KEY_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.SERVER_1_PEM_FILE; -import com.google.common.base.Strings; import com.google.common.util.concurrent.MoreExecutors; import io.envoyproxy.envoy.api.v2.auth.CertificateValidationContext; import io.envoyproxy.envoy.api.v2.auth.CommonTlsContext; -import io.envoyproxy.envoy.api.v2.auth.DownstreamTlsContext; import io.envoyproxy.envoy.api.v2.auth.TlsCertificate; import io.envoyproxy.envoy.api.v2.auth.UpstreamTlsContext; import io.envoyproxy.envoy.api.v2.core.DataSource; -import io.grpc.internal.testing.TestUtils; import io.netty.handler.ssl.SslContext; import java.io.IOException; import java.security.cert.CertStoreException; @@ -43,12 +45,6 @@ @RunWith(JUnit4.class) public class SecretVolumeSslContextProviderTest { - private static final String SERVER_1_PEM_FILE = "server1.pem"; - private static final String SERVER_1_KEY_FILE = "server1.key"; - private static final String CLIENT_PEM_FILE = "client.pem"; - private static final String CLIENT_KEY_FILE = "client.key"; - private static final String CA_PEM_FILE = "ca.pem"; - @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); @Test @@ -277,8 +273,8 @@ public void getProviderForServer_defaultTlsCertificate_throwsException() { TlsCertificate tlsCert = TlsCertificate.getDefaultInstance(); try { SecretVolumeSslContextProvider.getProviderForServer( - CommonTlsContextTestsUtil - .buildDownstreamTlsContext(getCommonTlsContext(tlsCert, /* certContext= */ null))); + CommonTlsContextTestsUtil.buildDownstreamTlsContext( + CommonTlsContextTestsUtil.getCommonTlsContext(tlsCert, /* certContext= */ null))); Assert.fail("no exception thrown"); } catch (IllegalArgumentException expected) { assertThat(expected).hasMessageThat().isEqualTo("filename expected"); @@ -298,8 +294,8 @@ public void getProviderForServer_certContextWithInlineString_throwsException() { .build(); try { SecretVolumeSslContextProvider.getProviderForServer( - CommonTlsContextTestsUtil - .buildDownstreamTlsContext(getCommonTlsContext(tlsCert, certContext))); + CommonTlsContextTestsUtil.buildDownstreamTlsContext( + CommonTlsContextTestsUtil.getCommonTlsContext(tlsCert, certContext))); Assert.fail("no exception thrown"); } catch (IllegalArgumentException expected) { assertThat(expected.getMessage()).isEqualTo("filename expected"); @@ -311,7 +307,9 @@ public void getProviderForClient_defaultCertContext_throwsException() { CertificateValidationContext certContext = CertificateValidationContext.getDefaultInstance(); try { SecretVolumeSslContextProvider.getProviderForClient( - buildUpstreamTlsContext(getCommonTlsContext(/* tlsCertificate= */ null, certContext))); + buildUpstreamTlsContext( + CommonTlsContextTestsUtil.getCommonTlsContext( + /* tlsCertificate= */ null, certContext))); Assert.fail("no exception thrown"); } catch (IllegalArgumentException expected) { assertThat(expected).hasMessageThat().isEqualTo("certContext is required"); @@ -331,7 +329,8 @@ public void getProviderForClient_certWithPrivateKeyInlineString_throwsException( .build(); try { SecretVolumeSslContextProvider.getProviderForClient( - buildUpstreamTlsContext(getCommonTlsContext(tlsCert, certContext))); + buildUpstreamTlsContext( + CommonTlsContextTestsUtil.getCommonTlsContext(tlsCert, certContext))); Assert.fail("no exception thrown"); } catch (IllegalArgumentException expected) { assertThat(expected).hasMessageThat().isEqualTo("filename expected"); @@ -351,38 +350,27 @@ public void getProviderForClient_certWithCertChainInlineString_throwsException() .build(); try { SecretVolumeSslContextProvider.getProviderForClient( - buildUpstreamTlsContext(getCommonTlsContext(tlsCert, certContext))); + buildUpstreamTlsContext( + CommonTlsContextTestsUtil.getCommonTlsContext(tlsCert, certContext))); Assert.fail("no exception thrown"); } catch (IllegalArgumentException expected) { assertThat(expected).hasMessageThat().isEqualTo("filename expected"); } } - private static String getTempFileNameForResourcesFile(String resFile) throws IOException { - return TestUtils.loadCert(resFile).getAbsolutePath(); - } - /** Helper method to build SecretVolumeSslContextProvider from given files. */ private static SecretVolumeSslContextProvider getSslContextSecretVolumeSecretProvider( - boolean server, String certChainFilename, String privateKeyFilename, String trustedCaFilename) - throws IOException { + boolean server, + String certChainFilename, + String privateKeyFilename, + String trustedCaFilename) { - // get temp file for each file - if (certChainFilename != null) { - certChainFilename = getTempFileNameForResourcesFile(certChainFilename); - } - if (privateKeyFilename != null) { - privateKeyFilename = getTempFileNameForResourcesFile(privateKeyFilename); - } - if (trustedCaFilename != null) { - trustedCaFilename = getTempFileNameForResourcesFile(trustedCaFilename); - } return server ? SecretVolumeSslContextProvider.getProviderForServer( - buildDownstreamTlsContextFromFilenames( - privateKeyFilename, certChainFilename, trustedCaFilename)) + CommonTlsContextTestsUtil.buildDownstreamTlsContextFromFilenames( + privateKeyFilename, certChainFilename, trustedCaFilename)) : SecretVolumeSslContextProvider.getProviderForClient( - buildUpstreamTlsContextFromFilenames( + CommonTlsContextTestsUtil.buildUpstreamTlsContextFromFilenames( privateKeyFilename, certChainFilename, trustedCaFilename)); } @@ -416,56 +404,6 @@ static void doChecksOnSslContext(boolean server, SslContext sslContext, } } - /** - * Helper method to build DownstreamTlsContext for above tests. Called from other classes as well. - */ - static DownstreamTlsContext buildDownstreamTlsContextFromFilenames( - String privateKey, String certChain, String trustCa) { - return CommonTlsContextTestsUtil.buildDownstreamTlsContext( - buildCommonTlsContextFromFilenames(privateKey, certChain, trustCa)); - } - - /** - * Helper method to build UpstreamTlsContext for above tests. Called from other classes as well. - */ - public static UpstreamTlsContext buildUpstreamTlsContextFromFilenames( - String privateKey, String certChain, String trustCa) { - return buildUpstreamTlsContext( - buildCommonTlsContextFromFilenames(privateKey, certChain, trustCa)); - } - - private static CommonTlsContext buildCommonTlsContextFromFilenames( - String privateKey, String certChain, String trustCa) { - TlsCertificate tlsCert = null; - if (!Strings.isNullOrEmpty(privateKey) && !Strings.isNullOrEmpty(certChain)) { - tlsCert = - TlsCertificate.newBuilder() - .setCertificateChain(DataSource.newBuilder().setFilename(certChain)) - .setPrivateKey(DataSource.newBuilder().setFilename(privateKey)) - .build(); - } - CertificateValidationContext certContext = null; - if (!Strings.isNullOrEmpty(trustCa)) { - certContext = - CertificateValidationContext.newBuilder() - .setTrustedCa(DataSource.newBuilder().setFilename(trustCa)) - .build(); - } - return getCommonTlsContext(tlsCert, certContext); - } - - private static CommonTlsContext getCommonTlsContext( - TlsCertificate tlsCertificate, CertificateValidationContext certContext) { - CommonTlsContext.Builder builder = CommonTlsContext.newBuilder(); - if (tlsCertificate != null) { - builder = builder.addTlsCertificates(tlsCertificate); - } - if (certContext != null) { - builder = builder.setValidationContext(certContext); - } - return builder.build(); - } - /** * Helper method to build UpstreamTlsContext for above tests. Called from other classes as well. */ diff --git a/xds/src/test/java/io/grpc/xds/internal/sds/ServerSslContextProviderFactoryTest.java b/xds/src/test/java/io/grpc/xds/internal/sds/ServerSslContextProviderFactoryTest.java index b61c629e439..e21bc55f2cb 100644 --- a/xds/src/test/java/io/grpc/xds/internal/sds/ServerSslContextProviderFactoryTest.java +++ b/xds/src/test/java/io/grpc/xds/internal/sds/ServerSslContextProviderFactoryTest.java @@ -17,6 +17,9 @@ package io.grpc.xds.internal.sds; import static com.google.common.truth.Truth.assertThat; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.CA_PEM_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.SERVER_1_KEY_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.SERVER_1_PEM_FILE; import io.envoyproxy.envoy.api.v2.auth.CommonTlsContext; import io.envoyproxy.envoy.api.v2.auth.DownstreamTlsContext; @@ -29,18 +32,14 @@ @RunWith(JUnit4.class) public class ServerSslContextProviderFactoryTest { - private static final String SERVER_PEM_FILE = "server1.pem"; - private static final String SERVER_KEY_FILE = "server1.key"; - private static final String CA_PEM_FILE = "ca.pem"; - ServerSslContextProviderFactory serverSslContextProviderFactory = new ServerSslContextProviderFactory(); @Test public void createSslContextProvider_allFilenames() { DownstreamTlsContext downstreamTlsContext = - SecretVolumeSslContextProviderTest.buildDownstreamTlsContextFromFilenames( - SERVER_KEY_FILE, SERVER_PEM_FILE, CA_PEM_FILE); + CommonTlsContextTestsUtil.buildDownstreamTlsContextFromFilenames( + SERVER_1_KEY_FILE, SERVER_1_PEM_FILE, CA_PEM_FILE); SslContextProvider sslContextProvider = serverSslContextProviderFactory.createSslContextProvider(downstreamTlsContext); @@ -70,7 +69,7 @@ public void createSslContextProvider_sdsConfigForTlsCert_expectException() { public void createSslContextProvider_sdsConfigForCertValidationContext_expectException() { CommonTlsContext commonTlsContext = CommonTlsContextTestsUtil.buildCommonTlsContextFromSdsConfigForValidationContext( - "name", "unix:/tmp/sds/path", SERVER_KEY_FILE, SERVER_PEM_FILE); + "name", "unix:/tmp/sds/path", SERVER_1_KEY_FILE, SERVER_1_PEM_FILE); DownstreamTlsContext downstreamTlsContext = CommonTlsContextTestsUtil.buildDownstreamTlsContext(commonTlsContext); diff --git a/xds/src/test/java/io/grpc/xds/internal/sds/TlsContextManagerTest.java b/xds/src/test/java/io/grpc/xds/internal/sds/TlsContextManagerTest.java index 253005ed8da..0c3617f11e5 100644 --- a/xds/src/test/java/io/grpc/xds/internal/sds/TlsContextManagerTest.java +++ b/xds/src/test/java/io/grpc/xds/internal/sds/TlsContextManagerTest.java @@ -17,6 +17,13 @@ package io.grpc.xds.internal.sds; import static com.google.common.truth.Truth.assertThat; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.CA_PEM_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.CLIENT_KEY_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.CLIENT_PEM_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.SERVER_0_KEY_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.SERVER_0_PEM_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.SERVER_1_KEY_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.SERVER_1_PEM_FILE; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; @@ -40,14 +47,6 @@ @RunWith(JUnit4.class) public class TlsContextManagerTest { - private static final String SERVER_0_PEM_FILE = "server0.pem"; - private static final String SERVER_0_KEY_FILE = "server0.key"; - private static final String SERVER_1_PEM_FILE = "server1.pem"; - private static final String SERVER_1_KEY_FILE = "server1.key"; - private static final String CLIENT_PEM_FILE = "client.pem"; - private static final String CLIENT_KEY_FILE = "client.key"; - private static final String CA_PEM_FILE = "ca.pem"; - @Rule public final MockitoRule mockitoRule = MockitoJUnit.rule(); @Mock @@ -66,7 +65,7 @@ public void clearInstance() throws NoSuchFieldException, IllegalAccessException @Test public void createServerSslContextProvider() { DownstreamTlsContext downstreamTlsContext = - SecretVolumeSslContextProviderTest.buildDownstreamTlsContextFromFilenames( + CommonTlsContextTestsUtil.buildDownstreamTlsContextFromFilenames( SERVER_1_KEY_FILE, SERVER_1_PEM_FILE, /* trustCa= */ null); TlsContextManagerImpl tlsContextManagerImpl = TlsContextManagerImpl.getInstance(); @@ -82,7 +81,7 @@ public void createServerSslContextProvider() { @Test public void createClientSslContextProvider() { UpstreamTlsContext upstreamTlsContext = - SecretVolumeSslContextProviderTest.buildUpstreamTlsContextFromFilenames( + CommonTlsContextTestsUtil.buildUpstreamTlsContextFromFilenames( /* privateKey= */ null, /* certChain= */ null, CA_PEM_FILE); TlsContextManagerImpl tlsContextManagerImpl = TlsContextManagerImpl.getInstance(); @@ -98,7 +97,7 @@ public void createClientSslContextProvider() { @Test public void createServerSslContextProvider_differentInstance() { DownstreamTlsContext downstreamTlsContext = - SecretVolumeSslContextProviderTest.buildDownstreamTlsContextFromFilenames( + CommonTlsContextTestsUtil.buildDownstreamTlsContextFromFilenames( SERVER_1_KEY_FILE, SERVER_1_PEM_FILE, /* trustCa= */ null); TlsContextManagerImpl tlsContextManagerImpl = TlsContextManagerImpl.getInstance(); @@ -107,7 +106,7 @@ public void createServerSslContextProvider_differentInstance() { assertThat(serverSecretProvider).isNotNull(); DownstreamTlsContext downstreamTlsContext1 = - SecretVolumeSslContextProviderTest.buildDownstreamTlsContextFromFilenames( + CommonTlsContextTestsUtil.buildDownstreamTlsContextFromFilenames( SERVER_0_KEY_FILE, SERVER_0_PEM_FILE, CA_PEM_FILE); SslContextProvider serverSecretProvider1 = tlsContextManagerImpl.findOrCreateServerSslContextProvider(downstreamTlsContext1); @@ -118,7 +117,7 @@ public void createServerSslContextProvider_differentInstance() { @Test public void createClientSslContextProvider_differentInstance() { UpstreamTlsContext upstreamTlsContext = - SecretVolumeSslContextProviderTest.buildUpstreamTlsContextFromFilenames( + CommonTlsContextTestsUtil.buildUpstreamTlsContextFromFilenames( /* privateKey= */ null, /* certChain= */ null, CA_PEM_FILE); TlsContextManagerImpl tlsContextManagerImpl = TlsContextManagerImpl.getInstance(); @@ -127,7 +126,7 @@ public void createClientSslContextProvider_differentInstance() { assertThat(clientSecretProvider).isNotNull(); UpstreamTlsContext upstreamTlsContext1 = - SecretVolumeSslContextProviderTest.buildUpstreamTlsContextFromFilenames( + CommonTlsContextTestsUtil.buildUpstreamTlsContextFromFilenames( CLIENT_KEY_FILE, CLIENT_PEM_FILE, CA_PEM_FILE); SslContextProvider clientSecretProvider1 = @@ -138,7 +137,7 @@ public void createClientSslContextProvider_differentInstance() { @Test public void createServerSslContextProvider_releaseInstance() { DownstreamTlsContext downstreamTlsContext = - SecretVolumeSslContextProviderTest.buildDownstreamTlsContextFromFilenames( + CommonTlsContextTestsUtil.buildDownstreamTlsContextFromFilenames( SERVER_1_KEY_FILE, SERVER_1_PEM_FILE, /* trustCa= */ null); TlsContextManagerImpl tlsContextManagerImpl = @@ -158,7 +157,7 @@ public void createServerSslContextProvider_releaseInstance() { @Test public void createClientSslContextProvider_releaseInstance() { UpstreamTlsContext upstreamTlsContext = - SecretVolumeSslContextProviderTest.buildUpstreamTlsContextFromFilenames( + CommonTlsContextTestsUtil.buildUpstreamTlsContextFromFilenames( CLIENT_KEY_FILE, CLIENT_PEM_FILE, CA_PEM_FILE); TlsContextManagerImpl tlsContextManagerImpl = diff --git a/xds/src/test/java/io/grpc/xds/internal/sds/XdsSdsClientServerTest.java b/xds/src/test/java/io/grpc/xds/internal/sds/XdsSdsClientServerTest.java deleted file mode 100644 index f3063c4436e..00000000000 --- a/xds/src/test/java/io/grpc/xds/internal/sds/XdsSdsClientServerTest.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright 2019 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.sds; - -import static com.google.common.truth.Truth.assertThat; - -import com.google.protobuf.BoolValue; -import io.envoyproxy.envoy.api.v2.auth.CertificateValidationContext; -import io.envoyproxy.envoy.api.v2.auth.CommonTlsContext; -import io.envoyproxy.envoy.api.v2.auth.DownstreamTlsContext; -import io.envoyproxy.envoy.api.v2.auth.TlsCertificate; -import io.envoyproxy.envoy.api.v2.auth.UpstreamTlsContext; -import io.envoyproxy.envoy.api.v2.core.DataSource; -import io.grpc.Server; -import io.grpc.internal.testing.TestUtils; -import io.grpc.stub.StreamObserver; -import io.grpc.testing.GrpcCleanupRule; -import io.grpc.testing.protobuf.SimpleRequest; -import io.grpc.testing.protobuf.SimpleResponse; -import io.grpc.testing.protobuf.SimpleServiceGrpc; -import java.io.IOException; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -/** - * Unit tests for {@link XdsChannelBuilder} and {@link XdsServerBuilder} for plaintext/TLS/mTLS - * modes. - */ -@RunWith(JUnit4.class) -public class XdsSdsClientServerTest { - - @Rule public final GrpcCleanupRule cleanupRule = new GrpcCleanupRule(); - - @Test - public void plaintextClientServer() throws IOException { - Server server = getXdsServer(/* downstreamTlsContext= */ null); - buildClientAndTest( - /* upstreamTlsContext= */ null, /* overrideAuthority= */ null, "buddy", server.getPort()); - } - - /** TLS channel - no mTLS. */ - @Test - public void tlsClientServer_noClientAuthentication() throws IOException { - String server1Pem = TestUtils.loadCert("server1.pem").getAbsolutePath(); - String server1Key = TestUtils.loadCert("server1.key").getAbsolutePath(); - - TlsCertificate tlsCert = - TlsCertificate.newBuilder() - .setPrivateKey(DataSource.newBuilder().setFilename(server1Key).build()) - .setCertificateChain(DataSource.newBuilder().setFilename(server1Pem).build()) - .build(); - - CommonTlsContext commonTlsContext = - CommonTlsContext.newBuilder().addTlsCertificates(tlsCert).build(); - - DownstreamTlsContext downstreamTlsContext = - DownstreamTlsContext.newBuilder() - .setCommonTlsContext(commonTlsContext) - .setRequireClientCertificate(BoolValue.of(false)) - .build(); - - Server server = getXdsServer(downstreamTlsContext); - - // for TLS client doesn't need cert but needs trustCa - String trustCa = TestUtils.loadCert("ca.pem").getAbsolutePath(); - CertificateValidationContext certContext = - CertificateValidationContext.newBuilder() - .setTrustedCa(DataSource.newBuilder().setFilename(trustCa).build()) - .build(); - - CommonTlsContext commonTlsContext1 = - CommonTlsContext.newBuilder().setValidationContext(certContext).build(); - - UpstreamTlsContext upstreamTlsContext = - UpstreamTlsContext.newBuilder().setCommonTlsContext(commonTlsContext1).build(); - buildClientAndTest(upstreamTlsContext, "foo.test.google.fr", "buddy", server.getPort()); - } - - /** mTLS - client auth enabled. */ - @Test - public void mtlsClientServer_withClientAuthentication() throws IOException, InterruptedException { - String server1Pem = TestUtils.loadCert("server1.pem").getAbsolutePath(); - String server1Key = TestUtils.loadCert("server1.key").getAbsolutePath(); - String trustCa = TestUtils.loadCert("ca.pem").getAbsolutePath(); - - TlsCertificate tlsCert = - TlsCertificate.newBuilder() - .setPrivateKey(DataSource.newBuilder().setFilename(server1Key).build()) - .setCertificateChain(DataSource.newBuilder().setFilename(server1Pem).build()) - .build(); - - CertificateValidationContext certContext = - CertificateValidationContext.newBuilder() - .setTrustedCa(DataSource.newBuilder().setFilename(trustCa).build()) - .build(); - - CommonTlsContext commonTlsContext = - CommonTlsContext.newBuilder() - .addTlsCertificates(tlsCert) - .setValidationContext(certContext) - .build(); - - DownstreamTlsContext downstreamTlsContext = - DownstreamTlsContext.newBuilder() - .setCommonTlsContext(commonTlsContext) - .setRequireClientCertificate(BoolValue.of(false)) - .build(); - - Server server = getXdsServer(downstreamTlsContext); - - String clientPem = TestUtils.loadCert("client.pem").getAbsolutePath(); - String clientKey = TestUtils.loadCert("client.key").getAbsolutePath(); - - TlsCertificate tlsCert1 = - TlsCertificate.newBuilder() - .setPrivateKey(DataSource.newBuilder().setFilename(clientKey).build()) - .setCertificateChain(DataSource.newBuilder().setFilename(clientPem).build()) - .build(); - - CommonTlsContext commonTlsContext1 = - CommonTlsContext.newBuilder() - .addTlsCertificates(tlsCert1) - .setValidationContext(certContext) - .build(); - - UpstreamTlsContext upstreamTlsContext = - UpstreamTlsContext.newBuilder().setCommonTlsContext(commonTlsContext1).build(); - - buildClientAndTest(upstreamTlsContext, "foo.test.google.fr", "buddy", server.getPort()); - } - - private Server getXdsServer(DownstreamTlsContext downstreamTlsContext) throws IOException { - XdsServerBuilder serverBuilder = - XdsServerBuilder.forPort(0) // get unused port - .addService(new SimpleServiceImpl()) - .tlsContext(downstreamTlsContext); - return cleanupRule.register(serverBuilder.build()).start(); - } - - private void buildClientAndTest( - UpstreamTlsContext upstreamTlsContext, - String overrideAuthority, - String requestMessage, - int serverPort) { - - XdsChannelBuilder builder = - XdsChannelBuilder.forTarget("localhost:" + serverPort).tlsContext(upstreamTlsContext); - if (overrideAuthority != null) { - builder = builder.overrideAuthority(overrideAuthority); - } - SimpleServiceGrpc.SimpleServiceBlockingStub blockingStub = - SimpleServiceGrpc.newBlockingStub(cleanupRule.register(builder.build())); - String resp = unaryRpc(requestMessage, blockingStub); - assertThat(resp).isEqualTo("Hello " + requestMessage); - } - - /** Say hello to server. */ - private static String unaryRpc( - String requestMessage, SimpleServiceGrpc.SimpleServiceBlockingStub blockingStub) { - SimpleRequest request = SimpleRequest.newBuilder().setRequestMessage(requestMessage).build(); - SimpleResponse response = blockingStub.unaryRpc(request); - return response.getResponseMessage(); - } - - private static class SimpleServiceImpl extends SimpleServiceGrpc.SimpleServiceImplBase { - - @Override - public void unaryRpc(SimpleRequest req, StreamObserver responseObserver) { - SimpleResponse response = - SimpleResponse.newBuilder() - .setResponseMessage("Hello " + req.getRequestMessage()) - .build(); - responseObserver.onNext(response); - responseObserver.onCompleted(); - } - } -} diff --git a/xds/src/test/java/io/grpc/xds/internal/sds/trust/SdsTrustManagerFactoryTest.java b/xds/src/test/java/io/grpc/xds/internal/sds/trust/SdsTrustManagerFactoryTest.java index 466c90c3226..72baa2df9eb 100644 --- a/xds/src/test/java/io/grpc/xds/internal/sds/trust/SdsTrustManagerFactoryTest.java +++ b/xds/src/test/java/io/grpc/xds/internal/sds/trust/SdsTrustManagerFactoryTest.java @@ -17,6 +17,11 @@ package io.grpc.xds.internal.sds.trust; import static com.google.common.truth.Truth.assertThat; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.BAD_CLIENT_PEM_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.BAD_SERVER_PEM_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.CA_PEM_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.CLIENT_PEM_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.SERVER_1_PEM_FILE; import com.google.protobuf.ByteString; import io.envoyproxy.envoy.api.v2.auth.CertificateValidationContext; @@ -36,21 +41,6 @@ @RunWith(JUnit4.class) public class SdsTrustManagerFactoryTest { - /** Trust store cert. */ - private static final String CA_PEM_FILE = "ca.pem"; - - /** server cert. */ - private static final String SERVER_1_PEM_FILE = "server1.pem"; - - /** client cert. */ - private static final String CLIENT_PEM_FILE = "client.pem"; - - /** bad server cert. */ - private static final String BAD_SERVER_PEM_FILE = "badserver.pem"; - - /** bad client cert. */ - private static final String BAD_CLIENT_PEM_FILE = "badclient.pem"; - @Test public void constructor_fromFile() throws CertificateException, IOException, CertStoreException { SdsTrustManagerFactory factory = diff --git a/xds/src/test/java/io/grpc/xds/internal/sds/trust/SdsX509TrustManagerTest.java b/xds/src/test/java/io/grpc/xds/internal/sds/trust/SdsX509TrustManagerTest.java index 78cadd4453b..66ad6f8ad60 100644 --- a/xds/src/test/java/io/grpc/xds/internal/sds/trust/SdsX509TrustManagerTest.java +++ b/xds/src/test/java/io/grpc/xds/internal/sds/trust/SdsX509TrustManagerTest.java @@ -17,6 +17,10 @@ package io.grpc.xds.internal.sds.trust; import static com.google.common.truth.Truth.assertThat; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.BAD_SERVER_PEM_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.CA_PEM_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.CLIENT_PEM_FILE; +import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.SERVER_1_PEM_FILE; import static org.junit.Assert.fail; import static org.mockito.Mockito.CALLS_REAL_METHODS; import static org.mockito.Mockito.doReturn; @@ -52,18 +56,6 @@ @RunWith(JUnit4.class) public class SdsX509TrustManagerTest { - /** Trust store cert. */ - private static final String CA_PEM_FILE = "ca.pem"; - - /** server1 has 4 SANs. */ - private static final String SERVER_1_PEM_FILE = "server1.pem"; - - /** client has no SANs. */ - private static final String CLIENT_PEM_FILE = "client.pem"; - - /** Untrusted server. */ - private static final String BAD_SERVER_PEM_FILE = "badserver.pem"; - @Rule public final MockitoRule mockitoRule = MockitoJUnit.rule(); From 9618af90e5a28904d3d7f451bab82f3dbf21e4cf Mon Sep 17 00:00:00 2001 From: sanjaypujare Date: Wed, 15 Apr 2020 09:23:44 -0700 Subject: [PATCH 015/104] xds: remove UpstreamTlsContext from XdsChannelBuilder (#6924) --- .../internal/sds/SdsProtocolNegotiators.java | 29 +--- .../xds/internal/sds/XdsChannelBuilder.java | 16 +-- .../io/grpc/xds/XdsSdsClientServerTest.java | 136 +++++++++++++++--- .../sds/SdsProtocolNegotiatorsTest.java | 21 ++- 4 files changed, 138 insertions(+), 64 deletions(-) diff --git a/xds/src/main/java/io/grpc/xds/internal/sds/SdsProtocolNegotiators.java b/xds/src/main/java/io/grpc/xds/internal/sds/SdsProtocolNegotiators.java index c50fec650a2..877d41e50ab 100644 --- a/xds/src/main/java/io/grpc/xds/internal/sds/SdsProtocolNegotiators.java +++ b/xds/src/main/java/io/grpc/xds/internal/sds/SdsProtocolNegotiators.java @@ -62,13 +62,9 @@ private SdsProtocolNegotiators() { private static final AsciiString SCHEME = AsciiString.of("https"); - /** - * Returns a {@link ProtocolNegotiatorFactory} to be used on {@link NettyChannelBuilder}. Passing - * {@code null} for upstreamTlsContext will fall back to plaintext. - */ - public static ProtocolNegotiatorFactory clientProtocolNegotiatorFactory( - @Nullable UpstreamTlsContext upstreamTlsContext) { - return new ClientSdsProtocolNegotiatorFactory(upstreamTlsContext); + /** Returns a {@link ProtocolNegotiatorFactory} to be used on {@link NettyChannelBuilder}. */ + public static ProtocolNegotiatorFactory clientProtocolNegotiatorFactory() { + return new ClientSdsProtocolNegotiatorFactory(); } /** @@ -92,16 +88,9 @@ public static ProtocolNegotiator serverProtocolNegotiator( private static final class ClientSdsProtocolNegotiatorFactory implements InternalNettyChannelBuilder.ProtocolNegotiatorFactory { - private final UpstreamTlsContext upstreamTlsContext; - - ClientSdsProtocolNegotiatorFactory(UpstreamTlsContext upstreamTlsContext) { - this.upstreamTlsContext = upstreamTlsContext; - } - @Override public InternalProtocolNegotiator.ProtocolNegotiator buildProtocolNegotiator() { - final ClientSdsProtocolNegotiator negotiator = - new ClientSdsProtocolNegotiator(upstreamTlsContext); + final ClientSdsProtocolNegotiator negotiator = new ClientSdsProtocolNegotiator(); final class LocalSdsNegotiator implements InternalProtocolNegotiator.ProtocolNegotiator { @Override @@ -127,13 +116,6 @@ public void close() { @VisibleForTesting static final class ClientSdsProtocolNegotiator implements ProtocolNegotiator { - // TODO (sanjaypujare) remove once we get this from CDS & don't need for testing - UpstreamTlsContext upstreamTlsContext; - - ClientSdsProtocolNegotiator(UpstreamTlsContext upstreamTlsContext) { - this.upstreamTlsContext = upstreamTlsContext; - } - @Override public AsciiString scheme() { return SCHEME; @@ -144,9 +126,6 @@ public ChannelHandler newHandler(GrpcHttp2ConnectionHandler grpcHandler) { // check if UpstreamTlsContext was passed via attributes UpstreamTlsContext localUpstreamTlsContext = grpcHandler.getEagAttributes().get(XdsAttributes.ATTR_UPSTREAM_TLS_CONTEXT); - if (localUpstreamTlsContext == null) { - localUpstreamTlsContext = upstreamTlsContext; - } if (isTlsContextEmpty(localUpstreamTlsContext)) { return InternalProtocolNegotiators.plaintext().newHandler(grpcHandler); } diff --git a/xds/src/main/java/io/grpc/xds/internal/sds/XdsChannelBuilder.java b/xds/src/main/java/io/grpc/xds/internal/sds/XdsChannelBuilder.java index 49d5f5411b1..2c80fc9da89 100644 --- a/xds/src/main/java/io/grpc/xds/internal/sds/XdsChannelBuilder.java +++ b/xds/src/main/java/io/grpc/xds/internal/sds/XdsChannelBuilder.java @@ -16,7 +16,6 @@ package io.grpc.xds.internal.sds; -import io.envoyproxy.envoy.api.v2.auth.UpstreamTlsContext; import io.grpc.ForwardingChannelBuilder; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; @@ -24,7 +23,6 @@ import io.grpc.netty.NettyChannelBuilder; import java.net.SocketAddress; import javax.annotation.CheckReturnValue; -import javax.annotation.Nullable; /** * A version of {@link ManagedChannelBuilder} to create xDS managed channels that will use SDS to @@ -34,9 +32,6 @@ public final class XdsChannelBuilder extends ForwardingChannelBuilder delegate() { return delegate; @@ -85,7 +71,7 @@ protected ManagedChannelBuilder delegate() { @Override public ManagedChannel build() { InternalNettyChannelBuilder.setProtocolNegotiatorFactory( - delegate, SdsProtocolNegotiators.clientProtocolNegotiatorFactory(upstreamTlsContext)); + delegate, SdsProtocolNegotiators.clientProtocolNegotiatorFactory()); return delegate.build(); } } diff --git a/xds/src/test/java/io/grpc/xds/XdsSdsClientServerTest.java b/xds/src/test/java/io/grpc/xds/XdsSdsClientServerTest.java index 8d1e09ff463..552eac98247 100644 --- a/xds/src/test/java/io/grpc/xds/XdsSdsClientServerTest.java +++ b/xds/src/test/java/io/grpc/xds/XdsSdsClientServerTest.java @@ -27,9 +27,12 @@ import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.SERVER_1_PEM_FILE; import static org.junit.Assert.fail; +import com.google.common.collect.ImmutableList; import io.envoyproxy.envoy.api.v2.auth.DownstreamTlsContext; import io.envoyproxy.envoy.api.v2.auth.UpstreamTlsContext; -import io.grpc.Server; +import io.grpc.Attributes; +import io.grpc.EquivalentAddressGroup; +import io.grpc.NameResolver; import io.grpc.StatusRuntimeException; import io.grpc.stub.StreamObserver; import io.grpc.testing.GrpcCleanupRule; @@ -41,8 +44,14 @@ import io.grpc.xds.internal.sds.XdsChannelBuilder; import io.grpc.xds.internal.sds.XdsServerBuilder; import java.io.IOException; +import java.net.Inet4Address; +import java.net.InetSocketAddress; import java.net.ServerSocket; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import javax.net.ssl.SSLHandshakeException; import org.junit.Before; import org.junit.Rule; @@ -66,8 +75,8 @@ public void setUp() throws IOException { } @Test - public void plaintextClientServer() throws IOException { - Server unused = buildServerWithTlsContext(/* downstreamTlsContext= */ null); + public void plaintextClientServer() throws IOException, URISyntaxException { + buildServerWithTlsContext(/* downstreamTlsContext= */ null); SimpleServiceGrpc.SimpleServiceBlockingStub blockingStub = getBlockingStub(/* upstreamTlsContext= */ null, /* overrideAuthority= */ null); @@ -76,11 +85,11 @@ public void plaintextClientServer() throws IOException { /** TLS channel - no mTLS. */ @Test - public void tlsClientServer_noClientAuthentication() throws IOException { + public void tlsClientServer_noClientAuthentication() throws IOException, URISyntaxException { DownstreamTlsContext downstreamTlsContext = CommonTlsContextTestsUtil.buildDownstreamTlsContextFromFilenames( SERVER_1_KEY_FILE, SERVER_1_PEM_FILE, null); - Server unused = buildServerWithTlsContext(downstreamTlsContext); + buildServerWithTlsContext(downstreamTlsContext); // for TLS, client only needs trustCa UpstreamTlsContext upstreamTlsContext = @@ -94,7 +103,7 @@ public void tlsClientServer_noClientAuthentication() throws IOException { /** mTLS - client auth enabled. */ @Test - public void mtlsClientServer_withClientAuthentication() throws IOException { + public void mtlsClientServer_withClientAuthentication() throws IOException, URISyntaxException { UpstreamTlsContext upstreamTlsContext = CommonTlsContextTestsUtil.buildUpstreamTlsContextFromFilenames( CLIENT_KEY_FILE, CLIENT_PEM_FILE, CA_PEM_FILE); @@ -103,7 +112,8 @@ public void mtlsClientServer_withClientAuthentication() throws IOException { /** mTLS - client auth enabled then update server certs to untrusted. */ @Test - public void mtlsClientServer_changeServerContext_expectException() throws IOException { + public void mtlsClientServer_changeServerContext_expectException() + throws IOException, URISyntaxException { UpstreamTlsContext upstreamTlsContext = CommonTlsContextTestsUtil.buildUpstreamTlsContextFromFilenames( CLIENT_KEY_FILE, CLIENT_PEM_FILE, CA_PEM_FILE); @@ -126,7 +136,7 @@ public void mtlsClientServer_changeServerContext_expectException() throws IOExce } private XdsClient.ListenerWatcher performMtlsTestAndGetListenerWatcher( - UpstreamTlsContext upstreamTlsContext) throws IOException { + UpstreamTlsContext upstreamTlsContext) throws IOException, URISyntaxException { DownstreamTlsContext downstreamTlsContext = CommonTlsContextTestsUtil.buildDownstreamTlsContextFromFilenames( SERVER_1_KEY_FILE, SERVER_1_PEM_FILE, CA_PEM_FILE); @@ -136,7 +146,7 @@ private XdsClient.ListenerWatcher performMtlsTestAndGetListenerWatcher( port, /* downstreamTlsContext= */ downstreamTlsContext); SdsProtocolNegotiators.ServerSdsProtocolNegotiator serverSdsProtocolNegotiator = new SdsProtocolNegotiators.ServerSdsProtocolNegotiator(xdsClientWrapperForServerSds); - Server unused = getServer(port, serverSdsProtocolNegotiator); + buildServer(port, serverSdsProtocolNegotiator); XdsClient.ListenerWatcher listenerWatcher = xdsClientWrapperForServerSds.getListenerWatcher(); @@ -146,21 +156,21 @@ private XdsClient.ListenerWatcher performMtlsTestAndGetListenerWatcher( return listenerWatcher; } - private Server buildServerWithTlsContext(DownstreamTlsContext downstreamTlsContext) + private void buildServerWithTlsContext(DownstreamTlsContext downstreamTlsContext) throws IOException { final XdsClientWrapperForServerSds xdsClientWrapperForServerSds = XdsClientWrapperForServerSdsTest.createXdsClientWrapperForServerSds( port, /* downstreamTlsContext= */ downstreamTlsContext); SdsProtocolNegotiators.ServerSdsProtocolNegotiator serverSdsProtocolNegotiator = new SdsProtocolNegotiators.ServerSdsProtocolNegotiator(xdsClientWrapperForServerSds); - return getServer(port, serverSdsProtocolNegotiator); + buildServer(port, serverSdsProtocolNegotiator); } - private Server getServer( + private void buildServer( int port, SdsProtocolNegotiators.ServerSdsProtocolNegotiator serverSdsProtocolNegotiator) throws IOException { XdsServerBuilder builder = XdsServerBuilder.forPort(port).addService(new SimpleServiceImpl()); - return cleanupRule.register(builder.buildServer(serverSdsProtocolNegotiator)).start(); + cleanupRule.register(builder.buildServer(serverSdsProtocolNegotiator)).start(); } private static int findFreePort() throws IOException { @@ -181,13 +191,28 @@ static EnvoyServerProtoData.Listener buildListener( } private SimpleServiceGrpc.SimpleServiceBlockingStub getBlockingStub( - UpstreamTlsContext upstreamTlsContext, String overrideAuthority) { - XdsChannelBuilder builder = - XdsChannelBuilder.forTarget("localhost:" + port).tlsContext(upstreamTlsContext); + final UpstreamTlsContext upstreamTlsContext, String overrideAuthority) + throws URISyntaxException { + URI expectedUri = new URI("sdstest://localhost:" + port); + FakeNameResolverFactory fakeNameResolverFactory = new FakeNameResolverFactory.Builder( + expectedUri).build(); + XdsChannelBuilder channelBuilder = + XdsChannelBuilder.forTarget("sdstest://localhost:" + port) + .nameResolverFactory(fakeNameResolverFactory); if (overrideAuthority != null) { - builder = builder.overrideAuthority(overrideAuthority); + channelBuilder = channelBuilder.overrideAuthority(overrideAuthority); } - return SimpleServiceGrpc.newBlockingStub(cleanupRule.register(builder.build())); + InetSocketAddress socketAddress = + new InetSocketAddress(Inet4Address.getLoopbackAddress(), port); + Attributes attrs = + (upstreamTlsContext != null) + ? Attributes.newBuilder() + .set(XdsAttributes.ATTR_UPSTREAM_TLS_CONTEXT, upstreamTlsContext) + .build() + : Attributes.EMPTY; + fakeNameResolverFactory.setServers( + ImmutableList.of(new EquivalentAddressGroup(socketAddress, attrs))); + return SimpleServiceGrpc.newBlockingStub(cleanupRule.register(channelBuilder.build())); } /** Say hello to server. */ @@ -210,4 +235,79 @@ public void unaryRpc(SimpleRequest req, StreamObserver responseO responseObserver.onCompleted(); } } + + private static final class FakeNameResolverFactory extends NameResolver.Factory { + final URI expectedUri; + List servers = ImmutableList.of(); + final ArrayList resolvers = new ArrayList<>(); + + FakeNameResolverFactory(URI expectedUri) { + this.expectedUri = expectedUri; + } + + void setServers(List servers) { + this.servers = servers; + } + + @Override + public NameResolver newNameResolver(final URI targetUri, NameResolver.Args args) { + if (!expectedUri.equals(targetUri)) { + return null; + } + FakeNameResolver resolver = new FakeNameResolver(); + resolvers.add(resolver); + return resolver; + } + + @Override + public String getDefaultScheme() { + return "sdstest"; + } + + final class FakeNameResolver extends NameResolver { + Listener2 listener; + + @Override + public String getServiceAuthority() { + return expectedUri.getAuthority(); + } + + @Override + public void start(Listener2 listener) { + this.listener = listener; + resolved(); + } + + @Override + public void refresh() { + resolved(); + } + + void resolved() { + ResolutionResult.Builder builder = ResolutionResult.newBuilder().setAddresses(servers); + listener.onResult(builder.build()); + } + + @Override + public void shutdown() { + } + + @Override + public String toString() { + return "FakeNameResolver"; + } + } + + static final class Builder { + final URI expectedUri; + + Builder(URI expectedUri) { + this.expectedUri = expectedUri; + } + + FakeNameResolverFactory build() { + return new FakeNameResolverFactory(expectedUri); + } + } + } } diff --git a/xds/src/test/java/io/grpc/xds/internal/sds/SdsProtocolNegotiatorsTest.java b/xds/src/test/java/io/grpc/xds/internal/sds/SdsProtocolNegotiatorsTest.java index b7a9336ccfb..4c500653e93 100644 --- a/xds/src/test/java/io/grpc/xds/internal/sds/SdsProtocolNegotiatorsTest.java +++ b/xds/src/test/java/io/grpc/xds/internal/sds/SdsProtocolNegotiatorsTest.java @@ -24,6 +24,8 @@ import static io.grpc.xds.internal.sds.CommonTlsContextTestsUtil.SERVER_1_PEM_FILE; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import com.google.common.base.Strings; import io.envoyproxy.envoy.api.v2.auth.CertificateValidationContext; @@ -32,10 +34,12 @@ import io.envoyproxy.envoy.api.v2.auth.TlsCertificate; import io.envoyproxy.envoy.api.v2.auth.UpstreamTlsContext; import io.envoyproxy.envoy.api.v2.core.DataSource; +import io.grpc.Attributes; import io.grpc.internal.testing.TestUtils; import io.grpc.netty.GrpcHttp2ConnectionHandler; import io.grpc.netty.InternalProtocolNegotiationEvent; import io.grpc.netty.InternalProtocolNegotiator; +import io.grpc.xds.XdsAttributes; import io.grpc.xds.XdsClientWrapperForServerSds; import io.grpc.xds.XdsClientWrapperForServerSdsTest; import io.grpc.xds.internal.sds.SdsProtocolNegotiators.ClientSdsHandler; @@ -143,9 +147,8 @@ private static CommonTlsContext getCommonTlsContext( } @Test - public void clientSdsProtocolNegotiatorNewHandler_nullTlsContext() { - ClientSdsProtocolNegotiator pn = - new ClientSdsProtocolNegotiator(/* upstreamTlsContext= */ null); + public void clientSdsProtocolNegotiatorNewHandler_noTlsContextAttribute() { + ClientSdsProtocolNegotiator pn = new ClientSdsProtocolNegotiator(); ChannelHandler newHandler = pn.newHandler(grpcHandler); assertThat(newHandler).isNotNull(); // ProtocolNegotiators.WaitUntilActiveHandler not accessible, get canonical name @@ -154,12 +157,18 @@ public void clientSdsProtocolNegotiatorNewHandler_nullTlsContext() { } @Test - public void clientSdsProtocolNegotiatorNewHandler_nonNullTlsContext() { + public void clientSdsProtocolNegotiatorNewHandler_withTlsContextAttribute() { UpstreamTlsContext upstreamTlsContext = buildUpstreamTlsContext( getCommonTlsContext(/* tlsCertificate= */ null, /* certContext= */ null)); - ClientSdsProtocolNegotiator pn = new ClientSdsProtocolNegotiator(upstreamTlsContext); - ChannelHandler newHandler = pn.newHandler(grpcHandler); + ClientSdsProtocolNegotiator pn = new ClientSdsProtocolNegotiator(); + GrpcHttp2ConnectionHandler mockHandler = mock(GrpcHttp2ConnectionHandler.class); + when(mockHandler.getEagAttributes()) + .thenReturn( + Attributes.newBuilder() + .set(XdsAttributes.ATTR_UPSTREAM_TLS_CONTEXT, upstreamTlsContext) + .build()); + ChannelHandler newHandler = pn.newHandler(mockHandler); assertThat(newHandler).isNotNull(); assertThat(newHandler).isInstanceOf(ClientSdsHandler.class); } From 2478912d794e0b35c73210de1c9686c762a3bf28 Mon Sep 17 00:00:00 2001 From: Chengyuan Zhang Date: Wed, 15 Apr 2020 20:12:01 +0000 Subject: [PATCH 016/104] xds: use separate LB configs for EDS policy running with different code paths (#6895) The LB configs used for EDS policy diverges for the full xDS flow (generated by CDS policy) and EDS-only flow (received in service config). This change creates a separate config (EdsConfig) for the actual EDS LB policy. CDS policy generates EdsConfig directly and the wrapper policy (i.e., XdsLoadBalancer) converts received XdsConfig to EdsConfig for EDS-only flow. --- .../java/io/grpc/xds/CdsLoadBalancer.java | 13 ++-- .../java/io/grpc/xds/EdsLoadBalancer.java | 53 +++++++------- .../io/grpc/xds/EdsLoadBalancerProvider.java | 63 ++++++++++++++++- .../java/io/grpc/xds/XdsLoadBalancer.java | 25 +++++-- .../io/grpc/xds/XdsLoadBalancerProvider.java | 69 +++++++------------ .../java/io/grpc/xds/CdsLoadBalancerTest.java | 29 ++++---- .../java/io/grpc/xds/EdsLoadBalancerTest.java | 50 ++++++-------- .../test/java/io/grpc/xds/FallbackLbTest.java | 27 +++----- .../grpc/xds/XdsLoadBalancerProviderTest.java | 10 ++- .../java/io/grpc/xds/XdsLoadBalancerTest.java | 44 ++++++++++-- 10 files changed, 221 insertions(+), 162 deletions(-) diff --git a/xds/src/main/java/io/grpc/xds/CdsLoadBalancer.java b/xds/src/main/java/io/grpc/xds/CdsLoadBalancer.java index 81cf5dcfa30..6e183684d8c 100644 --- a/xds/src/main/java/io/grpc/xds/CdsLoadBalancer.java +++ b/xds/src/main/java/io/grpc/xds/CdsLoadBalancer.java @@ -35,9 +35,9 @@ import io.grpc.util.ForwardingLoadBalancerHelper; import io.grpc.util.GracefulSwitchLoadBalancer; import io.grpc.xds.CdsLoadBalancerProvider.CdsConfig; +import io.grpc.xds.EdsLoadBalancerProvider.EdsConfig; import io.grpc.xds.XdsClient.ClusterUpdate; import io.grpc.xds.XdsClient.ClusterWatcher; -import io.grpc.xds.XdsLoadBalancerProvider.XdsConfig; import io.grpc.xds.XdsLogger.XdsLogLevel; import io.grpc.xds.XdsSubchannelPickers.ErrorPicker; import io.grpc.xds.internal.sds.SslContextProvider; @@ -287,13 +287,12 @@ public void onClusterChanged(ClusterUpdate newUpdate) { LoadBalancerProvider lbProvider = lbRegistry.getProvider(newUpdate.getLbPolicy()); Object lbConfig = lbProvider.parseLoadBalancingPolicyConfig(ImmutableMap.of()).getConfig(); - final XdsConfig edsConfig = - new XdsConfig( - /* cluster = */ newUpdate.getClusterName(), - new PolicySelection(lbProvider, ImmutableMap.of(), lbConfig), - /* fallbackPolicy = */ null, + final EdsConfig edsConfig = + new EdsConfig( + /* clusterName = */ newUpdate.getClusterName(), /* edsServiceName = */ newUpdate.getEdsServiceName(), - /* lrsServerName = */ newUpdate.getLrsServerName()); + /* lrsServerName = */ newUpdate.getLrsServerName(), + new PolicySelection(lbProvider, ImmutableMap.of(), lbConfig)); updateSslContextProvider(newUpdate.getUpstreamTlsContext()); if (edsBalancer == null) { edsBalancer = lbRegistry.getProvider(EDS_POLICY_NAME).newLoadBalancer(helper); diff --git a/xds/src/main/java/io/grpc/xds/EdsLoadBalancer.java b/xds/src/main/java/io/grpc/xds/EdsLoadBalancer.java index ae848c22b6d..835a59c01e2 100644 --- a/xds/src/main/java/io/grpc/xds/EdsLoadBalancer.java +++ b/xds/src/main/java/io/grpc/xds/EdsLoadBalancer.java @@ -16,6 +16,7 @@ package io.grpc.xds; +import static com.google.api.client.util.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static io.grpc.ConnectivityState.TRANSIENT_FAILURE; @@ -34,6 +35,7 @@ import io.grpc.util.GracefulSwitchLoadBalancer; import io.grpc.xds.Bootstrapper.BootstrapInfo; import io.grpc.xds.Bootstrapper.ServerInfo; +import io.grpc.xds.EdsLoadBalancerProvider.EdsConfig; import io.grpc.xds.EnvoyProtoData.DropOverload; import io.grpc.xds.EnvoyProtoData.Locality; import io.grpc.xds.EnvoyProtoData.LocalityLbEndpoints; @@ -43,7 +45,6 @@ import io.grpc.xds.XdsClient.RefCountedXdsClientObjectPool; import io.grpc.xds.XdsClient.XdsChannelFactory; import io.grpc.xds.XdsClient.XdsClientFactory; -import io.grpc.xds.XdsLoadBalancerProvider.XdsConfig; import io.grpc.xds.XdsLogger.XdsLogLevel; import io.grpc.xds.XdsSubchannelPickers.ErrorPicker; import java.util.List; @@ -64,15 +65,14 @@ final class EdsLoadBalancer extends LoadBalancer { private final XdsChannelFactory channelFactory; private final Helper edsLbHelper; - // Most recent XdsConfig. - @Nullable - private XdsConfig xdsConfig; @Nullable private ObjectPool xdsClientPool; @Nullable private XdsClient xdsClient; @Nullable private String clusterName; + @Nullable + private EdsConfig config; EdsLoadBalancer(Helper edsLbHelper, ResourceUpdateCallback resourceUpdateCallback) { this( @@ -108,27 +108,25 @@ final class EdsLoadBalancer extends LoadBalancer { public void handleResolvedAddresses(ResolvedAddresses resolvedAddresses) { logger.log(XdsLogLevel.DEBUG, "Received resolution result: {0}", resolvedAddresses); Object lbConfig = resolvedAddresses.getLoadBalancingPolicyConfig(); - if (lbConfig == null) { - edsLbHelper.updateBalancingState( - TRANSIENT_FAILURE, - new ErrorPicker(Status.UNAVAILABLE.withDescription("Missing EDS lb config"))); - return; - } - XdsConfig newXdsConfig = (XdsConfig) lbConfig; + checkNotNull(lbConfig, "missing EDS lb config"); + EdsConfig newEdsConfig = (EdsConfig) lbConfig; if (logger.isLoggable(XdsLogLevel.INFO)) { logger.log( XdsLogLevel.INFO, - "Received EDS lb config: cluster={0}, child_policy={1}, fallback_policy={2}, " - + "eds_service_name={3}, report_load={4}", - newXdsConfig.cluster, - newXdsConfig.endpointPickingPolicy != null - ? newXdsConfig.endpointPickingPolicy.getProvider().getPolicyName() : "", - newXdsConfig.fallbackPolicy != null - ? newXdsConfig.fallbackPolicy.getProvider().getPolicyName() : "", - newXdsConfig.edsServiceName, - newXdsConfig.lrsServerName != null); + "Received EDS lb config: cluster={0}, child_policy={1}, " + + "eds_service_name={2}, report_load={3}", + newEdsConfig.clusterName, + newEdsConfig.endpointPickingPolicy.getProvider().getPolicyName(), + newEdsConfig.edsServiceName, + newEdsConfig.lrsServerName != null); + } + if (clusterName == null) { + clusterName = newEdsConfig.clusterName; + } else { + checkArgument( + clusterName.equals(newEdsConfig.clusterName), + "cluster name should not change"); } - if (xdsClientPool == null) { // Init xdsClientPool and xdsClient. // There are two usecases: @@ -188,19 +186,16 @@ XdsClient createXdsClient() { xdsClient = xdsClientPool.getObject(); } - // FIXME(chengyuanzhang): make cluster name required in XdsConfig. - clusterName = newXdsConfig.cluster != null ? newXdsConfig.cluster : edsLbHelper.getAuthority(); - // Note: childPolicy change will be handled in LocalityStore, to be implemented. // If edsServiceName in XdsConfig is changed, do a graceful switch. - if (xdsConfig == null - || !Objects.equals(newXdsConfig.edsServiceName, xdsConfig.edsServiceName)) { + if (config == null + || !Objects.equals(newEdsConfig.edsServiceName, config.edsServiceName)) { LoadBalancer.Factory clusterEndpointsLoadBalancerFactory = - new ClusterEndpointsBalancerFactory(newXdsConfig.edsServiceName); + new ClusterEndpointsBalancerFactory(newEdsConfig.edsServiceName); switchingLoadBalancer.switchTo(clusterEndpointsLoadBalancerFactory); } switchingLoadBalancer.handleResolvedAddresses(resolvedAddresses); - this.xdsConfig = newXdsConfig; + this.config = newEdsConfig; } @Override @@ -283,7 +278,7 @@ final class ClusterEndpointsBalancer extends LoadBalancer { @Override public void handleResolvedAddresses(ResolvedAddresses resolvedAddresses) { - XdsConfig config = (XdsConfig) resolvedAddresses.getLoadBalancingPolicyConfig(); + EdsConfig config = (EdsConfig) resolvedAddresses.getLoadBalancingPolicyConfig(); if (config.lrsServerName != null) { if (!config.lrsServerName.equals("")) { throw new AssertionError("Can only report load to the same management server"); diff --git a/xds/src/main/java/io/grpc/xds/EdsLoadBalancerProvider.java b/xds/src/main/java/io/grpc/xds/EdsLoadBalancerProvider.java index c7f4e12a807..30ea3974437 100644 --- a/xds/src/main/java/io/grpc/xds/EdsLoadBalancerProvider.java +++ b/xds/src/main/java/io/grpc/xds/EdsLoadBalancerProvider.java @@ -16,14 +16,19 @@ package io.grpc.xds; +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.base.MoreObjects; +import com.google.common.base.Objects; import io.grpc.Internal; import io.grpc.LoadBalancer; import io.grpc.LoadBalancer.Helper; import io.grpc.LoadBalancerProvider; -import io.grpc.LoadBalancerRegistry; import io.grpc.NameResolver.ConfigOrError; +import io.grpc.internal.ServiceConfigUtil.PolicySelection; import io.grpc.xds.EdsLoadBalancer.ResourceUpdateCallback; import java.util.Map; +import javax.annotation.Nullable; /** * The provider for the "eds" balancing policy. This class should not be directly referenced in @@ -67,7 +72,59 @@ public void onAllDrop() {} @Override public ConfigOrError parseLoadBalancingPolicyConfig( Map rawLoadBalancingPolicyConfig) { - return XdsLoadBalancerProvider.parseLoadBalancingConfigPolicy( - rawLoadBalancingPolicyConfig, LoadBalancerRegistry.getDefaultRegistry()); + throw new UnsupportedOperationException(); + } + + static final class EdsConfig { + + final String clusterName; + @Nullable + final String edsServiceName; + @Nullable + final String lrsServerName; + final PolicySelection endpointPickingPolicy; + + EdsConfig( + String clusterName, + @Nullable String edsServiceName, + @Nullable String lrsServerName, + PolicySelection endpointPickingPolicy) { + this.clusterName = checkNotNull(clusterName, "clusterName"); + this.edsServiceName = edsServiceName; + this.lrsServerName = lrsServerName; + this.endpointPickingPolicy = checkNotNull(endpointPickingPolicy, "endpointPickingPolicy"); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("clusterName", clusterName) + .add("edsServiceName", edsServiceName) + .add("lrsServerName", lrsServerName) + .add("endpointPickingPolicy", endpointPickingPolicy) + .toString(); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof EdsConfig)) { + return false; + } + EdsConfig that = (EdsConfig) obj; + return Objects.equal(this.clusterName, that.clusterName) + && Objects.equal(this.edsServiceName, that.edsServiceName) + && Objects.equal(this.lrsServerName, that.lrsServerName) + && Objects.equal(this.endpointPickingPolicy, that.endpointPickingPolicy); + } + + @Override + public int hashCode() { + return + Objects.hashCode( + clusterName, + edsServiceName, + lrsServerName, + endpointPickingPolicy); + } } } diff --git a/xds/src/main/java/io/grpc/xds/XdsLoadBalancer.java b/xds/src/main/java/io/grpc/xds/XdsLoadBalancer.java index a252df68cfa..d522eab0854 100644 --- a/xds/src/main/java/io/grpc/xds/XdsLoadBalancer.java +++ b/xds/src/main/java/io/grpc/xds/XdsLoadBalancer.java @@ -18,6 +18,7 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; +import static io.grpc.ConnectivityState.TRANSIENT_FAILURE; import com.google.common.annotations.VisibleForTesting; import io.grpc.ChannelLogger.ChannelLogLevel; @@ -28,6 +29,9 @@ import io.grpc.SynchronizationContext.ScheduledHandle; import io.grpc.util.ForwardingLoadBalancerHelper; import io.grpc.xds.EdsLoadBalancer.ResourceUpdateCallback; +import io.grpc.xds.EdsLoadBalancerProvider.EdsConfig; +import io.grpc.xds.XdsLoadBalancerProvider.XdsConfig; +import io.grpc.xds.XdsSubchannelPickers.ErrorPicker; import java.util.concurrent.TimeUnit; import javax.annotation.CheckForNull; import javax.annotation.Nullable; @@ -91,7 +95,7 @@ public void onAllDrop() { Helper helper, PrimaryLbFactory primaryLbFactory, LoadBalancer.Factory fallbackLbFactory) { - this.helper = helper; + this.helper = checkNotNull(helper, "helper"); this.primaryLb = primaryLbFactory.newLoadBalancer( new PrimaryLbHelper(), resourceUpdateCallback); this.fallbackLbFactory = fallbackLbFactory; @@ -107,7 +111,14 @@ public boolean canHandleEmptyAddressListFromNameResolution() { @Override public void handleResolvedAddresses(ResolvedAddresses resolvedAddresses) { this.resolvedAddresses = resolvedAddresses; - + Object lbConfig = resolvedAddresses.getLoadBalancingPolicyConfig(); + if (lbConfig == null) { + helper.updateBalancingState( + TRANSIENT_FAILURE, + new ErrorPicker(Status.UNAVAILABLE.withDescription("Missing xDS lb config"))); + return; + } + XdsConfig newXdsConfig = (XdsConfig) lbConfig; if (isInFallbackMode()) { fallbackLb.handleResolvedAddresses(this.resolvedAddresses); } @@ -125,8 +136,14 @@ public void run() { new EnterFallbackTask(), FALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS, helper.getScheduledExecutorService()); } - - primaryLb.handleResolvedAddresses(resolvedAddresses); + EdsConfig edsConfig = + new EdsConfig( + helper.getAuthority(), + newXdsConfig.edsServiceName, + newXdsConfig.lrsServerName, + newXdsConfig.childPolicy); + primaryLb.handleResolvedAddresses( + resolvedAddresses.toBuilder().setLoadBalancingPolicyConfig(edsConfig).build()); } @Override diff --git a/xds/src/main/java/io/grpc/xds/XdsLoadBalancerProvider.java b/xds/src/main/java/io/grpc/xds/XdsLoadBalancerProvider.java index 91403425d49..bf621d9dc11 100644 --- a/xds/src/main/java/io/grpc/xds/XdsLoadBalancerProvider.java +++ b/xds/src/main/java/io/grpc/xds/XdsLoadBalancerProvider.java @@ -32,7 +32,7 @@ import io.grpc.internal.ServiceConfigUtil; import io.grpc.internal.ServiceConfigUtil.LbConfig; import io.grpc.internal.ServiceConfigUtil.PolicySelection; -import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; import javax.annotation.Nullable; @@ -77,19 +77,14 @@ public ConfigOrError parseLoadBalancingPolicyConfig( static ConfigOrError parseLoadBalancingConfigPolicy( Map rawLoadBalancingPolicyConfig, LoadBalancerRegistry registry) { try { - String cluster = JsonUtil.getString(rawLoadBalancingPolicyConfig, "cluster"); - LbConfig roundRobinConfig = new LbConfig("round_robin", ImmutableMap.of()); - List endpointPickingConfigs = ServiceConfigUtil.unwrapLoadBalancingConfigList( - JsonUtil.getListOfObjects(rawLoadBalancingPolicyConfig, "endpointPickingPolicy")); - if (endpointPickingConfigs == null) { - endpointPickingConfigs = new ArrayList<>(1); - } else { - endpointPickingConfigs = new ArrayList<>(endpointPickingConfigs); + List childPolicyConfigs = ServiceConfigUtil.unwrapLoadBalancingConfigList( + JsonUtil.getListOfObjects(rawLoadBalancingPolicyConfig, "childPolicy")); + if (childPolicyConfigs == null || childPolicyConfigs.isEmpty()) { + childPolicyConfigs = Collections.singletonList(roundRobinConfig); } - endpointPickingConfigs.add(roundRobinConfig); ConfigOrError childConfigOrError = - ServiceConfigUtil.selectLbPolicyFromList(endpointPickingConfigs, registry); + ServiceConfigUtil.selectLbPolicyFromList(childPolicyConfigs, registry); if (childConfigOrError.getError() != null) { return childConfigOrError; } @@ -97,12 +92,9 @@ static ConfigOrError parseLoadBalancingConfigPolicy( List fallbackConfigs = ServiceConfigUtil.unwrapLoadBalancingConfigList( JsonUtil.getListOfObjects(rawLoadBalancingPolicyConfig, "fallbackPolicy")); - if (fallbackConfigs == null) { - fallbackConfigs = new ArrayList<>(1); - } else { - fallbackConfigs = new ArrayList<>(fallbackConfigs); + if (fallbackConfigs == null || fallbackConfigs.isEmpty()) { + fallbackConfigs = Collections.singletonList(roundRobinConfig); } - fallbackConfigs.add(roundRobinConfig); ConfigOrError fallbackConfigOrError = ServiceConfigUtil.selectLbPolicyFromList(fallbackConfigs, registry); if (fallbackConfigOrError.getError() != null) { @@ -114,7 +106,7 @@ static ConfigOrError parseLoadBalancingConfigPolicy( String lrsServerName = JsonUtil.getString(rawLoadBalancingPolicyConfig, "lrsLoadReportingServerName"); return ConfigOrError.fromConfig( - new XdsConfig(cluster, childPolicy, fallbackPolicy, edsServiceName, lrsServerName)); + new XdsConfig(edsServiceName, lrsServerName, childPolicy, fallbackPolicy)); } catch (RuntimeException e) { return ConfigOrError.fromError( Status.fromThrowable(e).withDescription( @@ -123,46 +115,34 @@ static ConfigOrError parseLoadBalancingConfigPolicy( } /** - * Represents a successfully parsed and validated LoadBalancingConfig for XDS. + * Represents a successfully parsed and validated LoadBalancingConfig for XDS LB policy. */ static final class XdsConfig { - // FIXME(chengyuanzhang): make cluster name required. - @Nullable - final String cluster; - final PolicySelection endpointPickingPolicy; - @Nullable - final PolicySelection fallbackPolicy; - // Optional. Name to use in EDS query. If not present, defaults to the server name from the - // target URI. @Nullable final String edsServiceName; - // Optional. LRS server to send load reports to. If not present, load reporting will be - // disabled. If set to the empty string, load reporting will be sent to the same server that - // we obtained CDS data from. @Nullable final String lrsServerName; + final PolicySelection childPolicy; // default to round_robin if not specified in proto + final PolicySelection fallbackPolicy; // default to round_robin if not specified in proto XdsConfig( - @Nullable String cluster, - PolicySelection endpointPickingPolicy, - @Nullable PolicySelection fallbackPolicy, @Nullable String edsServiceName, - @Nullable String lrsServerName) { - this.cluster = cluster; - this.endpointPickingPolicy = checkNotNull(endpointPickingPolicy, "endpointPickingPolicy"); - this.fallbackPolicy = fallbackPolicy; + @Nullable String lrsServerName, + PolicySelection childPolicy, + PolicySelection fallbackPolicy) { this.edsServiceName = edsServiceName; this.lrsServerName = lrsServerName; + this.childPolicy = checkNotNull(childPolicy, "childPolicy"); + this.fallbackPolicy = fallbackPolicy; } @Override public String toString() { return MoreObjects.toStringHelper(this) - .add("cluster", cluster) - .add("endpointPickingPolicy", endpointPickingPolicy) - .add("fallbackPolicy", fallbackPolicy) .add("edsServiceName", edsServiceName) .add("lrsServerName", lrsServerName) + .add("childPolicy", childPolicy) + .add("fallbackPolicy", fallbackPolicy) .toString(); } @@ -172,17 +152,16 @@ public boolean equals(Object obj) { return false; } XdsConfig that = (XdsConfig) obj; - return Objects.equal(this.cluster, that.cluster) - && Objects.equal(this.endpointPickingPolicy, that.endpointPickingPolicy) - && Objects.equal(this.fallbackPolicy, that.fallbackPolicy) - && Objects.equal(this.edsServiceName, that.edsServiceName) - && Objects.equal(this.lrsServerName, that.lrsServerName); + return Objects.equal(this.edsServiceName, that.edsServiceName) + && Objects.equal(this.lrsServerName, that.lrsServerName) + && Objects.equal(this.childPolicy, that.childPolicy) + && Objects.equal(this.fallbackPolicy, that.fallbackPolicy); } @Override public int hashCode() { return Objects.hashCode( - cluster, endpointPickingPolicy, fallbackPolicy, edsServiceName, lrsServerName); + edsServiceName, lrsServerName, childPolicy, fallbackPolicy); } } } diff --git a/xds/src/test/java/io/grpc/xds/CdsLoadBalancerTest.java b/xds/src/test/java/io/grpc/xds/CdsLoadBalancerTest.java index 7faa0154e0e..a137bb365d9 100644 --- a/xds/src/test/java/io/grpc/xds/CdsLoadBalancerTest.java +++ b/xds/src/test/java/io/grpc/xds/CdsLoadBalancerTest.java @@ -54,13 +54,13 @@ import io.grpc.internal.FakeClock; import io.grpc.internal.ServiceConfigUtil.PolicySelection; import io.grpc.xds.CdsLoadBalancerProvider.CdsConfig; +import io.grpc.xds.EdsLoadBalancerProvider.EdsConfig; import io.grpc.xds.XdsClient.ClusterUpdate; import io.grpc.xds.XdsClient.ClusterWatcher; import io.grpc.xds.XdsClient.EndpointUpdate; import io.grpc.xds.XdsClient.EndpointWatcher; import io.grpc.xds.XdsClient.RefCountedXdsClientObjectPool; import io.grpc.xds.XdsClient.XdsClientFactory; -import io.grpc.xds.XdsLoadBalancerProvider.XdsConfig; import io.grpc.xds.internal.sds.CommonTlsContextTestsUtil; import io.grpc.xds.internal.sds.SslContextProvider; import io.grpc.xds.internal.sds.TlsContextManager; @@ -251,14 +251,13 @@ public void handleCdsConfigs() { verify(edsLoadBalancer1).handleResolvedAddresses(resolvedAddressesCaptor1.capture()); PolicySelection roundRobinPolicy = new PolicySelection( fakeRoundRobinLbProvider, new HashMap(), "fake round robin config"); - XdsConfig expectedXdsConfig = new XdsConfig( + EdsConfig expectedEdsConfig = new EdsConfig( "foo.googleapis.com", - roundRobinPolicy, - null, "edsServiceFoo.googleapis.com", - null); + null, + roundRobinPolicy); ResolvedAddresses resolvedAddressesFoo = resolvedAddressesCaptor1.getValue(); - assertThat(resolvedAddressesFoo.getLoadBalancingPolicyConfig()).isEqualTo(expectedXdsConfig); + assertThat(resolvedAddressesFoo.getLoadBalancingPolicyConfig()).isEqualTo(expectedEdsConfig); assertThat(resolvedAddressesFoo.getAttributes().get(XdsAttributes.XDS_CLIENT_POOL)) .isSameInstanceAs(xdsClientPool); @@ -293,14 +292,13 @@ public void handleCdsConfigs() { LoadBalancer edsLoadBalancer2 = edsLoadBalancers.poll(); ArgumentCaptor resolvedAddressesCaptor2 = ArgumentCaptor.forClass(null); verify(edsLoadBalancer2).handleResolvedAddresses(resolvedAddressesCaptor2.capture()); - expectedXdsConfig = new XdsConfig( + expectedEdsConfig = new EdsConfig( "bar.googleapis.com", - roundRobinPolicy, - null, "edsServiceBar.googleapis.com", - "lrsBar.googleapis.com"); + "lrsBar.googleapis.com", + roundRobinPolicy); ResolvedAddresses resolvedAddressesBar = resolvedAddressesCaptor2.getValue(); - assertThat(resolvedAddressesBar.getLoadBalancingPolicyConfig()).isEqualTo(expectedXdsConfig); + assertThat(resolvedAddressesBar.getLoadBalancingPolicyConfig()).isEqualTo(expectedEdsConfig); assertThat(resolvedAddressesBar.getAttributes().get(XdsAttributes.XDS_CLIENT_POOL)) .isSameInstanceAs(xdsClientPool); @@ -322,14 +320,13 @@ public void handleCdsConfigs() { .setLbPolicy("round_robin") .build()); verify(edsLoadBalancer2, times(2)).handleResolvedAddresses(resolvedAddressesCaptor2.capture()); - expectedXdsConfig = new XdsConfig( + expectedEdsConfig = new EdsConfig( "bar.googleapis.com", - roundRobinPolicy, - null, "edsServiceBar2.googleapis.com", - null); + null, + roundRobinPolicy); ResolvedAddresses resolvedAddressesBar2 = resolvedAddressesCaptor2.getValue(); - assertThat(resolvedAddressesBar2.getLoadBalancingPolicyConfig()).isEqualTo(expectedXdsConfig); + assertThat(resolvedAddressesBar2.getLoadBalancingPolicyConfig()).isEqualTo(expectedEdsConfig); cdsLoadBalancer.shutdown(); verify(edsLoadBalancer2).shutdown(); diff --git a/xds/src/test/java/io/grpc/xds/EdsLoadBalancerTest.java b/xds/src/test/java/io/grpc/xds/EdsLoadBalancerTest.java index 0187dbad3b1..a5e68d3b8c9 100644 --- a/xds/src/test/java/io/grpc/xds/EdsLoadBalancerTest.java +++ b/xds/src/test/java/io/grpc/xds/EdsLoadBalancerTest.java @@ -75,10 +75,10 @@ import io.grpc.xds.Bootstrapper.ChannelCreds; import io.grpc.xds.Bootstrapper.ServerInfo; import io.grpc.xds.EdsLoadBalancer.ResourceUpdateCallback; +import io.grpc.xds.EdsLoadBalancerProvider.EdsConfig; import io.grpc.xds.LocalityStore.LocalityStoreFactory; import io.grpc.xds.XdsClient.EndpointUpdate; import io.grpc.xds.XdsClient.XdsChannelFactory; -import io.grpc.xds.XdsLoadBalancerProvider.XdsConfig; import java.net.InetSocketAddress; import java.util.ArrayDeque; import java.util.ArrayList; @@ -274,8 +274,7 @@ public void tearDown() { @Test public void handleNameResolutionErrorBeforeAndAfterEdsWorkding() { - deliverResolvedAddresses( - new XdsConfig(CLUSTER_NAME, fakeEndpointPickingPolicy, null, null, null)); + deliverResolvedAddresses(null, null, fakeEndpointPickingPolicy); // handleResolutionError() before receiving any endpoint update. edsLb.handleNameResolutionError(Status.DATA_LOSS.withDescription("fake status")); @@ -303,8 +302,7 @@ public void handleNameResolutionErrorBeforeAndAfterEdsWorkding() { public void handleEdsServiceNameChangeInXdsConfig() { assertThat(childHelpers).isEmpty(); - deliverResolvedAddresses( - new XdsConfig(CLUSTER_NAME, fakeEndpointPickingPolicy, null, "edsServiceName1", null)); + deliverResolvedAddresses("edsServiceName1", null, fakeEndpointPickingPolicy); ClusterLoadAssignment clusterLoadAssignment = buildClusterLoadAssignment("edsServiceName1", ImmutableList.of( @@ -324,8 +322,7 @@ public void handleEdsServiceNameChangeInXdsConfig() { assertLatestConnectivityState(CONNECTING); // Change edsServicename to edsServiceName2. - deliverResolvedAddresses( - new XdsConfig(CLUSTER_NAME, fakeEndpointPickingPolicy, null, "edsServiceName2", null)); + deliverResolvedAddresses("edsServiceName2", null, fakeEndpointPickingPolicy); // The old balancer was not READY, so it will be shutdown immediately. verify(childBalancer1).shutdown(); @@ -355,8 +352,7 @@ public PickResult pickSubchannel(PickSubchannelArgs args) { assertLatestSubchannelPicker(subchannel2); // Change edsServiceName to edsServiceName3. - deliverResolvedAddresses( - new XdsConfig(CLUSTER_NAME, fakeEndpointPickingPolicy, null, "edsServiceName3", null)); + deliverResolvedAddresses("edsServiceName3", null, fakeEndpointPickingPolicy); clusterLoadAssignment = buildClusterLoadAssignment("edsServiceName3", ImmutableList.of( @@ -382,8 +378,7 @@ public PickResult pickSubchannel(PickSubchannelArgs args) { assertLatestConnectivityState(CONNECTING); // Change edsServiceName to edsServiceName4. - deliverResolvedAddresses( - new XdsConfig(CLUSTER_NAME, fakeEndpointPickingPolicy, null, "edsServiceName4", null)); + deliverResolvedAddresses("edsServiceName4", null, fakeEndpointPickingPolicy); verify(childBalancer3).shutdown(); clusterLoadAssignment = @@ -411,8 +406,7 @@ public PickResult pickSubchannel(PickSubchannelArgs args) { assertLatestSubchannelPicker(subchannel4); // Change edsServiceName to edsServiceName5. - deliverResolvedAddresses( - new XdsConfig(CLUSTER_NAME, fakeEndpointPickingPolicy, null, "edsServiceName5", null)); + deliverResolvedAddresses("edsServiceName5", null, fakeEndpointPickingPolicy); clusterLoadAssignment = buildClusterLoadAssignment("edsServiceName5", ImmutableList.of( @@ -447,8 +441,7 @@ public PickResult pickSubchannel(PickSubchannelArgs args) { @Test public void firstAndSecondEdsResponseReceived_onWorkingCalledOnce() { - deliverResolvedAddresses( - new XdsConfig(CLUSTER_NAME, fakeEndpointPickingPolicy, null, null, null)); + deliverResolvedAddresses(null, null, fakeEndpointPickingPolicy); verify(resourceUpdateCallback, never()).onWorking(); @@ -482,8 +475,7 @@ public void firstAndSecondEdsResponseReceived_onWorkingCalledOnce() { @Test public void handleAllDropUpdates_pickersAreDropped() { - deliverResolvedAddresses( - new XdsConfig(CLUSTER_NAME, fakeEndpointPickingPolicy, null, null, null)); + deliverResolvedAddresses(null, null, fakeEndpointPickingPolicy); ClusterLoadAssignment clusterLoadAssignment = buildClusterLoadAssignment( CLUSTER_NAME, @@ -536,8 +528,7 @@ public PickResult pickSubchannel(PickSubchannelArgs args) { @Test public void handleLocalityAssignmentUpdates_pickersUpdatedFromChildBalancer() { - deliverResolvedAddresses( - new XdsConfig(CLUSTER_NAME, fakeEndpointPickingPolicy, null, null, null)); + deliverResolvedAddresses(null, null, fakeEndpointPickingPolicy); LbEndpoint endpoint11 = buildLbEndpoint("addr11.example.com", 8011, HEALTHY, 11); LbEndpoint endpoint12 = buildLbEndpoint("addr12.example.com", 8012, HEALTHY, 12); @@ -627,8 +618,7 @@ LocalityStore newLocalityStore( helper, resourceUpdateCallback, lbRegistry, localityStoreFactory, bootstrapper, channelFactory); - deliverResolvedAddresses( - new XdsConfig(CLUSTER_NAME, fakeEndpointPickingPolicy, null, "edsServiceName1", null)); + deliverResolvedAddresses("edsServiceName1", null, fakeEndpointPickingPolicy); assertThat(localityStores).hasSize(1); LocalityStore localityStore = localityStores.peekLast(); @@ -665,8 +655,7 @@ LocalityStore newLocalityStore( verify(localityStore).updateLocalityStore(endpointUpdate.getLocalityLbEndpointsMap()); // Change cluster name. - deliverResolvedAddresses( - new XdsConfig(CLUSTER_NAME, fakeEndpointPickingPolicy, null, "edsServiceName2", null)); + deliverResolvedAddresses("edsServiceName2", null, fakeEndpointPickingPolicy); assertThat(localityStores).hasSize(2); localityStore = localityStores.peekLast(); @@ -689,8 +678,7 @@ LocalityStore newLocalityStore( @Test public void verifyErrorPropagation_noPreviousEndpointUpdateReceived() { - deliverResolvedAddresses( - new XdsConfig(CLUSTER_NAME, fakeEndpointPickingPolicy, null, null, null)); + deliverResolvedAddresses(null, null, fakeEndpointPickingPolicy); verify(resourceUpdateCallback, never()).onError(); // Forwarding 20 seconds so that the xds client will deem EDS resource not available. @@ -701,8 +689,7 @@ public void verifyErrorPropagation_noPreviousEndpointUpdateReceived() { @Test public void verifyErrorPropagation_withPreviousEndpointUpdateReceived() { - deliverResolvedAddresses( - new XdsConfig(CLUSTER_NAME, fakeEndpointPickingPolicy, null, null, null)); + deliverResolvedAddresses(null, null, fakeEndpointPickingPolicy); // Endpoint update received. ClusterLoadAssignment clusterLoadAssignment = buildClusterLoadAssignment(CLUSTER_NAME, @@ -748,10 +735,15 @@ private static EndpointUpdate getEndpointUpdateFromClusterAssignment( return endpointUpdateBuilder.build(); } - private void deliverResolvedAddresses(XdsConfig xdsConfig) { + private void deliverResolvedAddresses( + @Nullable String edsServiceName, + @Nullable String lrsServerName, + PolicySelection endpointPickingPolicy) { + EdsConfig config = + new EdsConfig(CLUSTER_NAME, edsServiceName, lrsServerName, endpointPickingPolicy); ResolvedAddresses.Builder resolvedAddressBuilder = ResolvedAddresses.newBuilder() .setAddresses(ImmutableList.of()) - .setLoadBalancingPolicyConfig(xdsConfig); + .setLoadBalancingPolicyConfig(config); if (isFullFlow) { resolvedAddressBuilder.setAttributes( Attributes.newBuilder().set(XdsAttributes.XDS_CLIENT_POOL, diff --git a/xds/src/test/java/io/grpc/xds/FallbackLbTest.java b/xds/src/test/java/io/grpc/xds/FallbackLbTest.java index 45e7edb5155..f06c70ac049 100644 --- a/xds/src/test/java/io/grpc/xds/FallbackLbTest.java +++ b/xds/src/test/java/io/grpc/xds/FallbackLbTest.java @@ -104,7 +104,7 @@ public LoadBalancer newLoadBalancer(Helper helper) { private final List helpers2 = new ArrayList<>(); private final List balancers1 = new ArrayList<>(); private final List balancers2 = new ArrayList<>(); - private final PolicySelection fakeEndpointPickingPolicy = + private final PolicySelection fakeChildPolicy = new PolicySelection(mock(LoadBalancerProvider.class), null, new Object()); private LoadBalancer fallbackLb; @@ -132,10 +132,9 @@ public void handlePolicyChanges() { .setAddresses(eags11) .setLoadBalancingPolicyConfig(new XdsConfig( null, - fakeEndpointPickingPolicy, - new PolicySelection(fallbackProvider1, null, lbConfig11), null, - null)) + fakeChildPolicy, + new PolicySelection(fallbackProvider1, null, lbConfig11))) .build()); assertThat(helpers1).hasSize(1); @@ -158,10 +157,9 @@ public void handlePolicyChanges() { .setAddresses(eags12) .setLoadBalancingPolicyConfig(new XdsConfig( null, - fakeEndpointPickingPolicy, - new PolicySelection(fallbackProvider1, null, lbConfig12), null, - null)) + fakeChildPolicy, + new PolicySelection(fallbackProvider1, null, lbConfig12))) .build()); verify(balancer1).handleResolvedAddresses(ResolvedAddresses.newBuilder() @@ -182,10 +180,9 @@ public void handlePolicyChanges() { .setAddresses(eags21) .setLoadBalancingPolicyConfig(new XdsConfig( null, - fakeEndpointPickingPolicy, - new PolicySelection(fallbackProvider2, null, lbConfig21), null, - null)) + fakeChildPolicy, + new PolicySelection(fallbackProvider2, null, lbConfig21))) .build()); verify(balancer1).shutdown(); @@ -216,10 +213,9 @@ public void handlePolicyChanges() { .setAddresses(eags22) .setLoadBalancingPolicyConfig(new XdsConfig( null, - fakeEndpointPickingPolicy, - new PolicySelection(fallbackProvider2, null, lbConfig22), null, - null)) + fakeChildPolicy, + new PolicySelection(fallbackProvider2, null, lbConfig22))) .build()); verify(balancer2).handleResolvedAddresses(ResolvedAddresses.newBuilder() @@ -250,10 +246,9 @@ public void propagateAddressesToFallbackPolicy() { .setAddresses(eags) .setLoadBalancingPolicyConfig(new XdsConfig( null, - fakeEndpointPickingPolicy, - new PolicySelection(fallbackProvider1, null, lbConfig), null, - null)) + fakeChildPolicy, + new PolicySelection(fallbackProvider1, null, lbConfig))) .build()); LoadBalancer balancer1 = balancers1.get(0); diff --git a/xds/src/test/java/io/grpc/xds/XdsLoadBalancerProviderTest.java b/xds/src/test/java/io/grpc/xds/XdsLoadBalancerProviderTest.java index 8d9a42bd7d6..2bd2e085392 100644 --- a/xds/src/test/java/io/grpc/xds/XdsLoadBalancerProviderTest.java +++ b/xds/src/test/java/io/grpc/xds/XdsLoadBalancerProviderTest.java @@ -96,8 +96,7 @@ public void tearDown() { @Test public void parseLoadBalancingConfigPolicy() throws Exception { String rawLbConfig = "{" - + "\"cluster\" : \"foo.googleapis.com\"," - + "\"endpointPickingPolicy\" : " + + "\"childPolicy\" : " + " [{\"lbPolicy3\" : {\"key\" : \"val\"}}, {\"supported_1\" : {}}]," + "\"fallbackPolicy\" : []," + "\"edsServiceName\" : \"dns:///eds.service.com:8080\"," @@ -111,7 +110,8 @@ public void parseLoadBalancingConfigPolicy() throws Exception { assertThat(configOrError.getConfig()).isInstanceOf(XdsConfig.class); assertThat(configOrError.getConfig()).isEqualTo( new XdsConfig( - "foo.googleapis.com", + "dns:///eds.service.com:8080", + "dns:///lrs.service.com:8080", new PolicySelection(lbProvider1, ServiceConfigUtil.unwrapLoadBalancingConfig( checkObject(JsonParser.parse("{\"supported_1\" : {}}"))).getRawConfigValue(), @@ -119,9 +119,7 @@ public void parseLoadBalancingConfigPolicy() throws Exception { new PolicySelection( lbRegistry.getProvider("round_robin"), new HashMap(), - "no service config"), - "dns:///eds.service.com:8080", - "dns:///lrs.service.com:8080") + "no service config")) ); } diff --git a/xds/src/test/java/io/grpc/xds/XdsLoadBalancerTest.java b/xds/src/test/java/io/grpc/xds/XdsLoadBalancerTest.java index d2942834517..b77c09fdb1f 100644 --- a/xds/src/test/java/io/grpc/xds/XdsLoadBalancerTest.java +++ b/xds/src/test/java/io/grpc/xds/XdsLoadBalancerTest.java @@ -37,11 +37,15 @@ import io.grpc.LoadBalancer.ResolvedAddresses; import io.grpc.LoadBalancer.Subchannel; import io.grpc.LoadBalancer.SubchannelPicker; +import io.grpc.LoadBalancerProvider; import io.grpc.Status; import io.grpc.SynchronizationContext; import io.grpc.internal.FakeClock; +import io.grpc.internal.ServiceConfigUtil.PolicySelection; import io.grpc.xds.EdsLoadBalancer.ResourceUpdateCallback; +import io.grpc.xds.EdsLoadBalancerProvider.EdsConfig; import io.grpc.xds.XdsLoadBalancer.PrimaryLbFactory; +import io.grpc.xds.XdsLoadBalancerProvider.XdsConfig; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; @@ -51,6 +55,7 @@ import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; @@ -58,6 +63,8 @@ /** Unit tests for {@link XdsLoadBalancer}. */ @RunWith(JUnit4.class) public class XdsLoadBalancerTest { + private static final String AUTHORITY = "grpc-test.googleapis.com"; + @Rule public final ExpectedException thrown = ExpectedException.none(); @Rule @@ -84,6 +91,7 @@ public void uncaughtException(Thread t, Throwable e) { private final List fallbackLbs = new ArrayList<>(); private int requestConnectionTimes; + private int primaryLbHandleAddrsTimes; @Before public void setUp() { @@ -109,6 +117,7 @@ public LoadBalancer newLoadBalancer(Helper helper) { return fallbackLb; } }; + doReturn(AUTHORITY).when(helper).getAuthority(); doReturn(syncContext).when(helper).getSynchronizationContext(); doReturn(fakeClock.getScheduledExecutorService()).when(helper).getScheduledExecutorService(); doReturn(mock(ChannelLogger.class)).when(helper).getChannelLogger(); @@ -213,11 +222,14 @@ public void primarySeeingEdsResponseThenFailsBeforeTimeoutAtStartup() { public void fallbackWillHandleLastResolvedAddresses() { verifyNotInFallbackMode(); + PolicySelection childPolicy = + new PolicySelection(mock(LoadBalancerProvider.class), null, null); + PolicySelection fallbackPolicy = + new PolicySelection(mock(LoadBalancerProvider.class), null, null); ResolvedAddresses resolvedAddresses = ResolvedAddresses.newBuilder() .setAddresses(ImmutableList.of()) - .setAttributes( - Attributes.newBuilder().set(Attributes.Key.create("k"), new Object()).build()) - .setLoadBalancingPolicyConfig(new Object()) + .setLoadBalancingPolicyConfig( + new XdsConfig(null, null, childPolicy, fallbackPolicy)) .build(); xdsLoadBalancer.handleResolvedAddresses(resolvedAddresses); @@ -238,15 +250,24 @@ private void verifyInFallbackMode() { verify(primaryLb, times(++requestConnectionTimes)).requestConnection(); verify(fallbackLb).requestConnection(); + PolicySelection childPolicy = + new PolicySelection(mock(LoadBalancerProvider.class), null, null); + PolicySelection fallbackPolicy = + new PolicySelection(mock(LoadBalancerProvider.class), null, null); ResolvedAddresses resolvedAddresses = ResolvedAddresses.newBuilder() .setAddresses(ImmutableList.of()) .setAttributes( Attributes.newBuilder().set(Attributes.Key.create("k"), new Object()).build()) - .setLoadBalancingPolicyConfig(new Object()) + .setLoadBalancingPolicyConfig(new XdsConfig(null, null, childPolicy, fallbackPolicy)) .build(); xdsLoadBalancer.handleResolvedAddresses(resolvedAddresses); - verify(primaryLb).handleResolvedAddresses(same(resolvedAddresses)); verify(fallbackLb).handleResolvedAddresses(same(resolvedAddresses)); + ArgumentCaptor resolvedAddrCaptor = ArgumentCaptor.forClass(null); + verify(primaryLb, times(++primaryLbHandleAddrsTimes)) + .handleResolvedAddresses(resolvedAddrCaptor.capture()); + ResolvedAddresses capturedResolvedAddr = resolvedAddrCaptor.getValue(); + EdsConfig edsConfig = (EdsConfig) capturedResolvedAddr.getLoadBalancingPolicyConfig(); + assertThat(edsConfig.endpointPickingPolicy).isEqualTo(childPolicy); Status status = Status.DATA_LOSS.withDescription(""); xdsLoadBalancer.handleNameResolutionError(status); @@ -269,14 +290,23 @@ private void verifyNotInFallbackMode() { xdsLoadBalancer.requestConnection(); verify(primaryLb, times(++requestConnectionTimes)).requestConnection(); + PolicySelection childPolicy = + new PolicySelection(mock(LoadBalancerProvider.class), null, null); + PolicySelection fallbackPolicy = + new PolicySelection(mock(LoadBalancerProvider.class), null, null); ResolvedAddresses resolvedAddresses = ResolvedAddresses.newBuilder() .setAddresses(ImmutableList.of()) .setAttributes( Attributes.newBuilder().set(Attributes.Key.create("k"), new Object()).build()) - .setLoadBalancingPolicyConfig(new Object()) + .setLoadBalancingPolicyConfig(new XdsConfig(null, null, childPolicy, fallbackPolicy)) .build(); xdsLoadBalancer.handleResolvedAddresses(resolvedAddresses); - verify(primaryLb).handleResolvedAddresses(same(resolvedAddresses)); + ArgumentCaptor resolvedAddrCaptor = ArgumentCaptor.forClass(null); + verify(primaryLb, times(++primaryLbHandleAddrsTimes)) + .handleResolvedAddresses(resolvedAddrCaptor.capture()); + ResolvedAddresses capturedResolvedAddr = resolvedAddrCaptor.getValue(); + EdsConfig edsConfig = (EdsConfig) capturedResolvedAddr.getLoadBalancingPolicyConfig(); + assertThat(edsConfig.endpointPickingPolicy).isEqualTo(childPolicy); Status status = Status.DATA_LOSS.withDescription(""); xdsLoadBalancer.handleNameResolutionError(status); From 17b2b96d8a4f544cdfd421fe62460212c091f0c1 Mon Sep 17 00:00:00 2001 From: Kun Zhang Date: Wed, 15 Apr 2020 14:44:02 -0700 Subject: [PATCH 017/104] netty: log the selection of allocators (#6930) --- netty/src/main/java/io/grpc/netty/Utils.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/netty/src/main/java/io/grpc/netty/Utils.java b/netty/src/main/java/io/grpc/netty/Utils.java index 7a3cc760339..c5326185613 100644 --- a/netty/src/main/java/io/grpc/netty/Utils.java +++ b/netty/src/main/java/io/grpc/netty/Utils.java @@ -125,25 +125,36 @@ private static final class ByteBufAllocatorPreferHeapHolder { public static ByteBufAllocator getByteBufAllocator(boolean forceHeapBuffer) { if (Boolean.parseBoolean( System.getProperty("io.grpc.netty.useCustomAllocator", "true"))) { - if (forceHeapBuffer || !PooledByteBufAllocator.defaultPreferDirect()) { + boolean defaultPreferDirect = PooledByteBufAllocator.defaultPreferDirect(); + logger.log( + Level.FINE, + String.format( + "Using custom allocator: forceHeapBuffer=%s, defaultPreferDirect=%s", + forceHeapBuffer, + defaultPreferDirect)); + if (forceHeapBuffer || !defaultPreferDirect) { return ByteBufAllocatorPreferHeapHolder.allocator; } else { return ByteBufAllocatorPreferDirectHolder.allocator; } } else { + logger.log(Level.FINE, "Using default allocator"); return ByteBufAllocator.DEFAULT; } } private static ByteBufAllocator createByteBufAllocator(boolean preferDirect) { int maxOrder; + logger.log(Level.FINE, "Creating allocator, preferDirect=" + preferDirect); if (System.getProperty("io.netty.allocator.maxOrder") == null) { // See the implementation of PooledByteBufAllocator. DEFAULT_MAX_ORDER in there is // 11, which makes chunk size to be 8192 << 11 = 16 MiB. We want the chunk size to be // 2MiB, thus reducing the maxOrder to 8. maxOrder = 8; + logger.log(Level.FINE, "Forcing maxOrder=" + maxOrder); } else { maxOrder = PooledByteBufAllocator.defaultMaxOrder(); + logger.log(Level.FINE, "Using default maxOrder=" + maxOrder); } return new PooledByteBufAllocator( preferDirect, From 3bd141bf184a82a2cc92c98c906141d5b32ee88e Mon Sep 17 00:00:00 2001 From: ZHANG Dapeng Date: Thu, 16 Apr 2020 13:50:26 -0700 Subject: [PATCH 018/104] xds: allow duplicated route matcher and prefix='/' --- .../main/java/io/grpc/xds/EnvoyProtoData.java | 10 ++ .../main/java/io/grpc/xds/XdsClientImpl.java | 53 ++++----- .../java/io/grpc/xds/XdsNameResolver.java | 18 ++-- .../java/io/grpc/xds/XdsNameResolverTest.java | 101 +++++++++++++++++- 4 files changed, 138 insertions(+), 44 deletions(-) diff --git a/xds/src/main/java/io/grpc/xds/EnvoyProtoData.java b/xds/src/main/java/io/grpc/xds/EnvoyProtoData.java index 8afbe433b7f..d7f1c89f541 100644 --- a/xds/src/main/java/io/grpc/xds/EnvoyProtoData.java +++ b/xds/src/main/java/io/grpc/xds/EnvoyProtoData.java @@ -421,6 +421,16 @@ boolean hasRegex() { return hasRegex; } + boolean isDefaultMatcher() { + if (hasRegex) { + return false; + } + if (!path.isEmpty()) { + return false; + } + return prefix.isEmpty() || prefix.equals("/"); + } + @Override public boolean equals(Object o) { if (this == o) { diff --git a/xds/src/main/java/io/grpc/xds/XdsClientImpl.java b/xds/src/main/java/io/grpc/xds/XdsClientImpl.java index 5498a0c81b7..96dbc9b2d2b 100644 --- a/xds/src/main/java/io/grpc/xds/XdsClientImpl.java +++ b/xds/src/main/java/io/grpc/xds/XdsClientImpl.java @@ -916,8 +916,7 @@ private static String validateRoutes(List routes) { if (!enablePathMatching) { EnvoyProtoData.Route route = routes.get(routes.size() - 1); RouteMatch routeMatch = route.getRouteMatch(); - if (!routeMatch.getPath().isEmpty() || !routeMatch.getPrefix().isEmpty() - || routeMatch.hasRegex()) { + if (!routeMatch.isDefaultMatcher()) { return "The last route must be the default route"; } if (route.getRouteAction() == null) { @@ -934,50 +933,34 @@ private static String validateRoutes(List routes) { // For now we consider the whole list invalid if anything invalid for grpc is found. // TODO(zdapeng): Fix it if the decision is different from current implementation. // TODO(zdapeng): Add test for validation. - Set prefixMatches = new HashSet<>(); - Set pathMatches = new HashSet<>(); for (int i = 0; i < routes.size(); i++) { EnvoyProtoData.Route route = routes.get(i); - - if (route.getRouteAction() == null) { + RouteAction routeAction = route.getRouteAction(); + if (routeAction == null) { return "Route action is not specified for one of the routes"; } - RouteMatch routeMatch = route.getRouteMatch(); - String prefix = routeMatch.getPrefix(); - String path = routeMatch.getPath(); - if (!prefix.isEmpty()) { - if (!prefix.startsWith("/") || !prefix.endsWith("/") || prefix.length() < 3) { - return "Prefix route match must be in the format of '/service/'"; - } - if (prefixMatches.contains(prefix)) { - return "Duplicate prefix match found"; - } - prefixMatches.add(prefix); - } else if (!path.isEmpty()) { - int lastSlash = path.lastIndexOf('/'); - if (!path.startsWith("/") || lastSlash == 0 || lastSlash == path.length() - 1) { - return "Path route match must be in the format of '/service/method'"; - } - if (pathMatches.contains(path)) { - return "Duplicate path match found"; - } - pathMatches.add(path); - } else if (routeMatch.hasRegex()) { - return "Regex route match not supported"; - } else { // Default route match - if (i != routes.size() - 1) { - return "Default route found but is not the last route in the route list"; + if (!routeMatch.isDefaultMatcher()) { + String prefix = routeMatch.getPrefix(); + String path = routeMatch.getPath(); + if (!prefix.isEmpty()) { + if (!prefix.startsWith("/") || !prefix.endsWith("/") || prefix.length() < 3) { + return "Prefix route match must be in the format of '/service/'"; + } + } else if (!path.isEmpty()) { + int lastSlash = path.lastIndexOf('/'); + if (!path.startsWith("/") || lastSlash == 0 || lastSlash == path.length() - 1) { + return "Path route match must be in the format of '/service/method'"; + } + } else if (routeMatch.hasRegex()) { + return "Regex route match not supported"; } } - if (i == routes.size() - 1) { - if (!prefix.isEmpty() || !path.isEmpty()) { + if (!routeMatch.isDefaultMatcher()) { return "The last route must be the default route"; } } - - RouteAction routeAction = route.getRouteAction(); if (routeAction.getCluster().isEmpty() && routeAction.getWeightedCluster().isEmpty()) { return "Either cluster or weighted cluster route action must be provided"; } diff --git a/xds/src/main/java/io/grpc/xds/XdsNameResolver.java b/xds/src/main/java/io/grpc/xds/XdsNameResolver.java index 7f9d2a6cd9b..c503182197a 100644 --- a/xds/src/main/java/io/grpc/xds/XdsNameResolver.java +++ b/xds/src/main/java/io/grpc/xds/XdsNameResolver.java @@ -226,14 +226,16 @@ public void onError(Status error) { for (Route route : routesUpdate) { String service = ""; String method = ""; - String prefix = route.getRouteMatch().getPrefix(); - String path = route.getRouteMatch().getPath(); - if (!prefix.isEmpty()) { - service = prefix.substring(1, prefix.length() - 1); - } else if (!path.isEmpty()) { - int splitIndex = path.lastIndexOf('/'); - service = path.substring(1, splitIndex); - method = path.substring(splitIndex + 1); + if (!route.getRouteMatch().isDefaultMatcher()) { + String prefix = route.getRouteMatch().getPrefix(); + String path = route.getRouteMatch().getPath(); + if (!prefix.isEmpty()) { + service = prefix.substring(1, prefix.length() - 1); + } else if (!path.isEmpty()) { + int splitIndex = path.lastIndexOf('/'); + service = path.substring(1, splitIndex); + method = path.substring(splitIndex + 1); + } } Map methodName = ImmutableMap.of("service", service, "method", method); String actionName; diff --git a/xds/src/test/java/io/grpc/xds/XdsNameResolverTest.java b/xds/src/test/java/io/grpc/xds/XdsNameResolverTest.java index 883ecfa1b19..49e214e5474 100644 --- a/xds/src/test/java/io/grpc/xds/XdsNameResolverTest.java +++ b/xds/src/test/java/io/grpc/xds/XdsNameResolverTest.java @@ -376,8 +376,9 @@ public void resolve_resourceUpdated_multipleRoutes() { ImmutableMap.of( "cluster-bar.googleapis.com", 30, "cluster-bar2.googleapis.com", 70))) .build(), - // default, routed to cluster + // default with prefix = "/", routed to cluster Route.newBuilder() + .setMatch(RouteMatch.newBuilder().setPrefix("/")) .setRoute(buildClusterRoute("cluster-hello.googleapis.com")) .build()); HttpConnectionManager httpConnectionManager = @@ -477,6 +478,104 @@ public void resolve_resourceUpdated_multipleRoutes() { "cluster-foo.googleapis.com", 20, "cluster-bar.googleapis.com", 80)); } + @Test + @SuppressWarnings("unchecked") + public void resolve_resourceUpdated_allowDuplicateMatchers() { + XdsClientImpl.enablePathMatching = true; + xdsNameResolver.start(mockListener); + assertThat(responseObservers).hasSize(1); + StreamObserver responseObserver = responseObservers.poll(); + // Simulate receiving another LDS response that tells client to do RDS. + String routeConfigName = "route-foo.googleapis.com"; + responseObserver.onNext( + buildLdsResponseForRdsResource("1", AUTHORITY, routeConfigName, "0001")); + + // Client sent an RDS request for resource "route-foo.googleapis.com" (Omitted in this test). + List protoRoutes = + ImmutableList.of( + // path match, routed to cluster + Route.newBuilder() + .setMatch(buildPathMatch("fooSvc", "hello")) + .setRoute(buildClusterRoute("cluster-hello.googleapis.com")) + .build(), + // prefix match, routed to cluster + Route.newBuilder() + .setMatch(buildPrefixMatch("fooSvc")) + .setRoute(buildClusterRoute("cluster-foo.googleapis.com")) + .build(), + // duplicate path match, routed to weighted clusters + Route.newBuilder() + .setMatch(buildPathMatch("fooSvc", "hello")) + .setRoute(buildWeightedClusterRoute(ImmutableMap.of( + "cluster-hello.googleapis.com", 40, "cluster-hello2.googleapis.com", 60))) + .build(), + // duplicage prefix match, routed to weighted clusters + Route.newBuilder() + .setMatch(buildPrefixMatch("fooSvc")) + .setRoute( + buildWeightedClusterRoute( + ImmutableMap.of( + "cluster-bar.googleapis.com", 30, "cluster-bar2.googleapis.com", 70))) + .build(), + // default, routed to cluster + Route.newBuilder() + .setRoute(buildClusterRoute("cluster-hello.googleapis.com")) + .build()); + List routeConfigs = ImmutableList.of( + Any.pack( + buildRouteConfiguration( + routeConfigName, + ImmutableList.of(buildVirtualHostForRoutes(AUTHORITY, protoRoutes))))); + responseObserver.onNext( + buildDiscoveryResponse("0", routeConfigs, XdsClientImpl.ADS_TYPE_URL_RDS, "0000")); + + ArgumentCaptor resolutionResultCaptor = ArgumentCaptor.forClass(null); + verify(mockListener).onResult(resolutionResultCaptor.capture()); + ResolutionResult result = resolutionResultCaptor.getValue(); + assertThat(result.getAddresses()).isEmpty(); + Map serviceConfig = (Map) result.getServiceConfig().getConfig(); + + List> rawLbConfigs = + (List>) serviceConfig.get("loadBalancingConfig"); + Map lbConfig = Iterables.getOnlyElement(rawLbConfigs); + assertThat(lbConfig.keySet()).containsExactly("xds_routing_experimental"); + Map rawConfigValues = (Map) lbConfig.get("xds_routing_experimental"); + assertThat(rawConfigValues.keySet()).containsExactly("action", "route"); + Map> actions = + (Map>) rawConfigValues.get("action"); + List> routes = (List>) rawConfigValues.get("route"); + assertThat(routes).hasSize(5); + for (Map route : routes) { + assertThat(route.keySet()).containsExactly("methodName", "action"); + } + assertThat((Map) routes.get(0).get("methodName")) + .containsExactly("service", "fooSvc", "method", "hello"); + String action0 = (String) routes.get(0).get("action"); + assertThat((Map) routes.get(1).get("methodName")) + .containsExactly("service", "fooSvc", "method", ""); + String action1 = (String) routes.get(1).get("action"); + assertThat((Map) routes.get(2).get("methodName")) + .containsExactly("service", "fooSvc", "method", "hello"); + String action2 = (String) routes.get(2).get("action"); + assertThat((Map) routes.get(3).get("methodName")) + .containsExactly("service", "fooSvc", "method", ""); + String action3 = (String) routes.get(3).get("action"); + assertThat((Map) routes.get(4).get("methodName")) + .containsExactly("service", "", "method", ""); + String action4 = (String) routes.get(4).get("action"); + assertCdsPolicy(actions.get(action0), "cluster-hello.googleapis.com"); + assertCdsPolicy(actions.get(action1), "cluster-foo.googleapis.com"); + assertWeightedTargetPolicy( + actions.get(action2), + ImmutableMap.of( + "cluster-hello.googleapis.com", 40, "cluster-hello2.googleapis.com", 60)); + assertWeightedTargetPolicy( + actions.get(action3), + ImmutableMap.of( + "cluster-bar.googleapis.com", 30, "cluster-bar2.googleapis.com", 70)); + assertThat(action4).isEqualTo(action0); + } + /** Asserts that the given action contains a single CDS policy with the given cluster name. */ @SuppressWarnings("unchecked") private static void assertCdsPolicy(Map action, String clusterName) { From da855f4164e151bc0c679a856546e95f70092548 Mon Sep 17 00:00:00 2001 From: Patrice Chalin Date: Thu, 16 Apr 2020 16:55:05 -0400 Subject: [PATCH 019/104] Delete example-kotlin (#6936) --- .travis.yml | 1 - RELEASING.md | 2 - buildscripts/kokoro/android.sh | 3 - examples/README.md | 6 +- examples/example-kotlin/README.md | 62 ----------- .../android/helloworld/app/build.gradle | 81 -------------- .../android/helloworld/app/proguard-rules.pro | 17 --- .../app/src/main/AndroidManifest.xml | 22 ---- .../helloworldexample/HelloworldActivity.kt | 100 ------------------ .../app/src/main/proto/helloworld.proto | 37 ------- .../main/res/layout/activity_helloworld.xml | 54 ---------- .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 3418 -> 0 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 2206 -> 0 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 4842 -> 0 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 7718 -> 0 bytes .../app/src/main/res/values/strings.xml | 3 - .../android/helloworld/build.gradle | 27 ----- .../android/helloworld/settings.gradle | 1 - examples/example-kotlin/build.gradle | 73 ------------- examples/example-kotlin/settings.gradle | 12 --- .../examples/helloworld/HelloWorldClient.kt | 83 --------------- .../examples/helloworld/HelloWorldServer.kt | 89 ---------------- .../src/main/proto/helloworld.proto | 37 ------- .../helloworld/HelloWorldClientTest.kt | 99 ----------------- .../helloworld/HelloWorldServerTest.kt | 72 ------------- 25 files changed, 1 insertion(+), 880 deletions(-) delete mode 100644 examples/example-kotlin/README.md delete mode 100644 examples/example-kotlin/android/helloworld/app/build.gradle delete mode 100644 examples/example-kotlin/android/helloworld/app/proguard-rules.pro delete mode 100644 examples/example-kotlin/android/helloworld/app/src/main/AndroidManifest.xml delete mode 100644 examples/example-kotlin/android/helloworld/app/src/main/kotlin/io/grpc/helloworldexample/HelloworldActivity.kt delete mode 100644 examples/example-kotlin/android/helloworld/app/src/main/proto/helloworld.proto delete mode 100644 examples/example-kotlin/android/helloworld/app/src/main/res/layout/activity_helloworld.xml delete mode 100644 examples/example-kotlin/android/helloworld/app/src/main/res/mipmap-hdpi/ic_launcher.png delete mode 100644 examples/example-kotlin/android/helloworld/app/src/main/res/mipmap-mdpi/ic_launcher.png delete mode 100644 examples/example-kotlin/android/helloworld/app/src/main/res/mipmap-xhdpi/ic_launcher.png delete mode 100644 examples/example-kotlin/android/helloworld/app/src/main/res/mipmap-xxhdpi/ic_launcher.png delete mode 100644 examples/example-kotlin/android/helloworld/app/src/main/res/values/strings.xml delete mode 100644 examples/example-kotlin/android/helloworld/build.gradle delete mode 100644 examples/example-kotlin/android/helloworld/settings.gradle delete mode 100644 examples/example-kotlin/build.gradle delete mode 100644 examples/example-kotlin/settings.gradle delete mode 100644 examples/example-kotlin/src/main/kotlin/io/grpc/examples/helloworld/HelloWorldClient.kt delete mode 100644 examples/example-kotlin/src/main/kotlin/io/grpc/examples/helloworld/HelloWorldServer.kt delete mode 100644 examples/example-kotlin/src/main/proto/helloworld.proto delete mode 100644 examples/example-kotlin/src/test/kotlin/io/grpc/examples/helloworld/HelloWorldClientTest.kt delete mode 100644 examples/example-kotlin/src/test/kotlin/io/grpc/examples/helloworld/HelloWorldServerTest.kt diff --git a/.travis.yml b/.travis.yml index c81d28675a3..4a37defdafb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,7 +28,6 @@ install: - pushd examples/example-hostname && ../gradlew build && popd - pushd examples/example-hostname && mvn verify && popd - pushd examples/example-tls && ../gradlew clean build && popd - - pushd examples/example-kotlin && ../gradlew build && popd - pushd examples/example-xds && ../gradlew build && popd before_script: diff --git a/RELEASING.md b/RELEASING.md index 9d45bd9f5fe..1ed37a79059 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -46,8 +46,6 @@ $ VERSION_FILES=( examples/example-jwt-auth/pom.xml examples/example-hostname/build.gradle examples/example-hostname/pom.xml - examples/example-kotlin/build.gradle - examples/example-kotlin/android/helloworld/app/build.gradle examples/example-tls/build.gradle examples/example-tls/pom.xml examples/example-xds/build.gradle diff --git a/buildscripts/kokoro/android.sh b/buildscripts/kokoro/android.sh index 9751c9aa433..951488f747d 100755 --- a/buildscripts/kokoro/android.sh +++ b/buildscripts/kokoro/android.sh @@ -56,9 +56,6 @@ cd ../routeguide cd ../helloworld ../../gradlew build -cd "$BASE_DIR/github/grpc-java/examples/example-kotlin/android/helloworld/" -../../../gradlew build - # Skip APK size and dex count comparisons for non-PR builds if [[ -z "${KOKORO_GITHUB_PULL_REQUEST_COMMIT:-}" ]]; then diff --git a/examples/README.md b/examples/README.md index fdcec2d636b..9f74895a45c 100644 --- a/examples/README.md +++ b/examples/README.md @@ -158,10 +158,6 @@ $ bazel-bin/hello-world-client - [JWT-based Authentication](example-jwt-auth) -- [Kotlin examples](example-kotlin) - -- [Kotlin Android examples](example-kotlin/android) - ## Unit test examples Examples for unit testing gRPC clients and servers are located in [examples/src/test](src/test). @@ -174,7 +170,7 @@ examples to write unit tests. `InProcessTransport` is light-weight and runs the and client in the same process without any socket/TCP connection. Mocking the client stub provides a false sense of security when writing tests. Mocking stubs and responses -allows for tests that don't map to reality, causing the tests to pass, but the system-under-test to fail. +allows for tests that don't map to reality, causing the tests to pass, but the system-under-test to fail. The gRPC client library is complicated, and accurately reproducing that complexity with mocks is very hard. You will be better off and write less code by using `InProcessTransport` instead. diff --git a/examples/example-kotlin/README.md b/examples/example-kotlin/README.md deleted file mode 100644 index 0f396a925f2..00000000000 --- a/examples/example-kotlin/README.md +++ /dev/null @@ -1,62 +0,0 @@ -grpc Kotlin example -============================================== - -The examples require grpc-java to already be built. You are strongly encouraged -to check out a git release tag, since there will already be a build of grpc -available. Otherwise you must follow COMPILING.md. - -You may want to read through the -[Quick Start Guide](https://grpc.io/docs/quickstart/java.html) -before trying out the examples. - -To build the examples, - -1. **[Install gRPC Java library SNAPSHOT locally, including code generation plugin](../../COMPILING.md) (Only need this step for non-released versions, e.g. master HEAD).** - -2. Run in this directory: -``` -$ ../gradlew installDist -``` - -This creates the scripts `hello-world-server`, `hello-world-client`, -`route-guide-server`, and `route-guide-client` in the -`build/install/examples/bin/` directory that run the examples. Each -example requires the server to be running before starting the client. - -For example, to try the hello world example first run: - -``` -$ ./build/install/examples/bin/hello-world-server -``` - -And in a different terminal window run: - -``` -$ ./build/install/examples/bin/hello-world-client -``` - -That's it! - -Please refer to gRPC Java's [README](../README.md) and -[tutorial](https://grpc.io/docs/tutorials/basic/java.html) for more -information. - -Unit test examples -============================================== - -Examples for unit testing gRPC clients and servers are located in [./src/test](./src/test). - -In general, we DO NOT allow overriding the client stub. -We encourage users to leverage `InProcessTransport` as demonstrated in the examples to -write unit tests. `InProcessTransport` is light-weight and runs the server -and client in the same process without any socket/TCP connection. - -For testing a gRPC client, create the client with a real stub -using an InProcessChannelBuilder.java and test it against an InProcessServer.java -with a mock/fake service implementation. - -For testing a gRPC server, create the server as an InProcessServer, -and test it against a real client stub with an InProcessChannel. - -The gRPC-java library also provides a JUnit rule, GrpcCleanupRule.java, to do the graceful shutdown -boilerplate for you. diff --git a/examples/example-kotlin/android/helloworld/app/build.gradle b/examples/example-kotlin/android/helloworld/app/build.gradle deleted file mode 100644 index 9558f1a06fe..00000000000 --- a/examples/example-kotlin/android/helloworld/app/build.gradle +++ /dev/null @@ -1,81 +0,0 @@ -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-android-extensions' -apply plugin: 'com.google.protobuf' - -android { - compileSdkVersion 27 - - defaultConfig { - applicationId "io.grpc.helloworldexample" - // API level 14+ is required for TLS since Google Play Services v10.2 - minSdkVersion 14 - targetSdkVersion 27 - versionCode 1 - versionName "1.0" - } - buildTypes { - debug { minifyEnabled false } - release { - minifyEnabled true - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } - } - lintOptions { - disable 'GoogleAppIndexingWarning', 'HardcodedText', 'InvalidPackage' - textReport true - textOutput "stdout" - } - // Android Studio 3.1 does not automatically pick up '/kotlin' as source input - sourceSets { - main.java.srcDirs += 'src/main/kotlin' - test.java.srcDirs += 'src/test/kotlin' - androidTest.java.srcDirs += 'src/androidTest/kotlin' - } - - lintOptions { - // Do not complain about outdated deps, so that this can javax.annotation-api can be same - // as other projects in this repo. Your project is not required to do this, and can - // upgrade the dep. - disable 'GradleDependency' - // The Android linter does not correctly detect resources used in Kotlin. - // See: - // - https://youtrack.jetbrains.com/issue/KT-7729 - // - https://youtrack.jetbrains.com/issue/KT-12499 - disable 'UnusedResources' - textReport true - textOutput "stdout" - } -} - -protobuf { - protoc { artifact = 'com.google.protobuf:protoc:3.11.0' } - plugins { - grpc { artifact = 'io.grpc:protoc-gen-grpc-java:1.30.0-SNAPSHOT' // CURRENT_GRPC_VERSION - } - } - generateProtoTasks { - all().each { task -> - task.builtins { - java { option 'lite' } - } - task.plugins { - grpc { // Options added to --grpc_out - option 'lite' } - } - } - } -} - -dependencies { - implementation 'com.android.support:appcompat-v7:27.0.2' - implementation 'javax.annotation:javax.annotation-api:1.2' - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - - // You need to build grpc-java to obtain these libraries below. - implementation 'io.grpc:grpc-okhttp:1.30.0-SNAPSHOT' // CURRENT_GRPC_VERSION - implementation 'io.grpc:grpc-protobuf-lite:1.30.0-SNAPSHOT' // CURRENT_GRPC_VERSION - implementation 'io.grpc:grpc-stub:1.30.0-SNAPSHOT' // CURRENT_GRPC_VERSION -} - -repositories { mavenCentral() } diff --git a/examples/example-kotlin/android/helloworld/app/proguard-rules.pro b/examples/example-kotlin/android/helloworld/app/proguard-rules.pro deleted file mode 100644 index 1507a526787..00000000000 --- a/examples/example-kotlin/android/helloworld/app/proguard-rules.pro +++ /dev/null @@ -1,17 +0,0 @@ -# Add project specific ProGuard rules here. -# By default, the flags in this file are appended to flags specified -# in $ANDROID_HOME/tools/proguard/proguard-android.txt -# You can edit the include path and order by changing the proguardFiles -# directive in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# Add any project specific keep options here: - --dontwarn com.google.common.** -# Ignores: can't find referenced class javax.lang.model.element.Modifier --dontwarn com.google.errorprone.annotations.** --dontwarn javax.naming.** --dontwarn okio.** --dontwarn sun.misc.Unsafe diff --git a/examples/example-kotlin/android/helloworld/app/src/main/AndroidManifest.xml b/examples/example-kotlin/android/helloworld/app/src/main/AndroidManifest.xml deleted file mode 100644 index eee4057cd0c..00000000000 --- a/examples/example-kotlin/android/helloworld/app/src/main/AndroidManifest.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/examples/example-kotlin/android/helloworld/app/src/main/kotlin/io/grpc/helloworldexample/HelloworldActivity.kt b/examples/example-kotlin/android/helloworld/app/src/main/kotlin/io/grpc/helloworldexample/HelloworldActivity.kt deleted file mode 100644 index 66c96b9bc61..00000000000 --- a/examples/example-kotlin/android/helloworld/app/src/main/kotlin/io/grpc/helloworldexample/HelloworldActivity.kt +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2015 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.helloworldexample - -import android.app.Activity -import android.content.Context -import android.os.AsyncTask -import android.os.Bundle -import android.support.v7.app.AppCompatActivity -import android.text.TextUtils -import android.text.method.ScrollingMovementMethod -import android.view.View -import android.view.inputmethod.InputMethodManager -import android.widget.Button -import android.widget.TextView -import io.grpc.ManagedChannel -import io.grpc.ManagedChannelBuilder -import io.grpc.examples.helloworld.GreeterGrpc -import io.grpc.examples.helloworld.HelloRequest -import java.io.PrintWriter -import java.io.StringWriter -import java.lang.ref.WeakReference -import java.util.concurrent.TimeUnit -import kotlinx.android.synthetic.main.activity_helloworld.* - -class HelloworldActivity : AppCompatActivity(), View.OnClickListener { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_helloworld) - grpc_response_text!!.movementMethod = ScrollingMovementMethod() - send_button!!.setOnClickListener(this) - } - - override fun onClick(view: View) { - (getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager) - .hideSoftInputFromWindow(host_edit_text!!.windowToken, 0) - send_button!!.isEnabled = false - grpc_response_text!!.text = "" - GrpcTask(this) - .execute( - host_edit_text!!.text.toString(), - message_edit_text!!.text.toString(), - port_edit_text!!.text.toString()) - } - - private class GrpcTask constructor(activity: Activity) : AsyncTask() { - private val activityReference: WeakReference = WeakReference(activity) - private var channel: ManagedChannel? = null - - override fun doInBackground(vararg params: String): String { - val host = params[0] - val message = params[1] - val portStr = params[2] - val port = if (TextUtils.isEmpty(portStr)) 0 else Integer.valueOf(portStr) - return try { - channel = ManagedChannelBuilder.forAddress(host, port).usePlaintext().build() - val stub = GreeterGrpc.newBlockingStub(channel) - val request = HelloRequest.newBuilder().setName(message).build() - val reply = stub.sayHello(request) - reply.message - } catch (e: Exception) { - val sw = StringWriter() - val pw = PrintWriter(sw) - e.printStackTrace(pw) - pw.flush() - - "Failed... : %s".format(sw) - } - } - - override fun onPostExecute(result: String) { - try { - channel?.shutdown()?.awaitTermination(1, TimeUnit.SECONDS) - } catch (e: InterruptedException) { - Thread.currentThread().interrupt() - } - - val activity = activityReference.get() ?: return - val resultText: TextView = activity.findViewById(R.id.grpc_response_text) - val sendButton: Button = activity.findViewById(R.id.send_button) - - resultText.text = result - sendButton.isEnabled = true - } - } -} diff --git a/examples/example-kotlin/android/helloworld/app/src/main/proto/helloworld.proto b/examples/example-kotlin/android/helloworld/app/src/main/proto/helloworld.proto deleted file mode 100644 index c60d9416f1f..00000000000 --- a/examples/example-kotlin/android/helloworld/app/src/main/proto/helloworld.proto +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2015 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. -syntax = "proto3"; - -option java_multiple_files = true; -option java_package = "io.grpc.examples.helloworld"; -option java_outer_classname = "HelloWorldProto"; -option objc_class_prefix = "HLW"; - -package helloworld; - -// The greeting service definition. -service Greeter { - // Sends a greeting - rpc SayHello (HelloRequest) returns (HelloReply) {} -} - -// The request message containing the user's name. -message HelloRequest { - string name = 1; -} - -// The response message containing the greetings -message HelloReply { - string message = 1; -} diff --git a/examples/example-kotlin/android/helloworld/app/src/main/res/layout/activity_helloworld.xml b/examples/example-kotlin/android/helloworld/app/src/main/res/layout/activity_helloworld.xml deleted file mode 100644 index e9f41f46696..00000000000 --- a/examples/example-kotlin/android/helloworld/app/src/main/res/layout/activity_helloworld.xml +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - - - - - -