From 6d41b4283a4cc4cf1782b84527bb7c44da66ce7d Mon Sep 17 00:00:00 2001 From: Sergii Tkachenko Date: Wed, 20 Oct 2021 10:25:02 -0700 Subject: [PATCH 01/50] Start 1.43.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 ++-- examples/example-tls/build.gradle | 2 +- examples/example-tls/pom.xml | 4 ++-- examples/example-xds/build.gradle | 2 +- examples/pom.xml | 4 ++-- 22 files changed, 40 insertions(+), 40 deletions(-) diff --git a/build.gradle b/build.gradle index df487de8fc4..9d9e991bca5 100644 --- a/build.gradle +++ b/build.gradle @@ -18,7 +18,7 @@ subprojects { apply plugin: "net.ltgt.errorprone" group = "io.grpc" - version = "1.42.0-SNAPSHOT" // CURRENT_GRPC_VERSION + version = "1.43.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 3d1476a5bee..98dcbf87587 100644 --- a/compiler/src/test/golden/TestDeprecatedService.java.txt +++ b/compiler/src/test/golden/TestDeprecatedService.java.txt @@ -8,7 +8,7 @@ import static io.grpc.MethodDescriptor.generateFullMethodName; * */ @javax.annotation.Generated( - value = "by gRPC proto compiler (version 1.42.0-SNAPSHOT)", + value = "by gRPC proto compiler (version 1.43.0-SNAPSHOT)", comments = "Source: grpc/testing/compiler/test.proto") @io.grpc.stub.annotations.GrpcGenerated @java.lang.Deprecated diff --git a/compiler/src/test/golden/TestService.java.txt b/compiler/src/test/golden/TestService.java.txt index 929ab5af817..49938bd9345 100644 --- a/compiler/src/test/golden/TestService.java.txt +++ b/compiler/src/test/golden/TestService.java.txt @@ -8,7 +8,7 @@ import static io.grpc.MethodDescriptor.generateFullMethodName; * */ @javax.annotation.Generated( - value = "by gRPC proto compiler (version 1.42.0-SNAPSHOT)", + value = "by gRPC proto compiler (version 1.43.0-SNAPSHOT)", comments = "Source: grpc/testing/compiler/test.proto") @io.grpc.stub.annotations.GrpcGenerated public final class TestServiceGrpc { diff --git a/compiler/src/testLite/golden/TestDeprecatedService.java.txt b/compiler/src/testLite/golden/TestDeprecatedService.java.txt index e088d7c0ede..fdd980a554e 100644 --- a/compiler/src/testLite/golden/TestDeprecatedService.java.txt +++ b/compiler/src/testLite/golden/TestDeprecatedService.java.txt @@ -8,7 +8,7 @@ import static io.grpc.MethodDescriptor.generateFullMethodName; * */ @javax.annotation.Generated( - value = "by gRPC proto compiler (version 1.42.0-SNAPSHOT)", + value = "by gRPC proto compiler (version 1.43.0-SNAPSHOT)", comments = "Source: grpc/testing/compiler/test.proto") @io.grpc.stub.annotations.GrpcGenerated @java.lang.Deprecated diff --git a/compiler/src/testLite/golden/TestService.java.txt b/compiler/src/testLite/golden/TestService.java.txt index 17005100271..e13628ac66f 100644 --- a/compiler/src/testLite/golden/TestService.java.txt +++ b/compiler/src/testLite/golden/TestService.java.txt @@ -8,7 +8,7 @@ import static io.grpc.MethodDescriptor.generateFullMethodName; * */ @javax.annotation.Generated( - value = "by gRPC proto compiler (version 1.42.0-SNAPSHOT)", + value = "by gRPC proto compiler (version 1.43.0-SNAPSHOT)", comments = "Source: grpc/testing/compiler/test.proto") @io.grpc.stub.annotations.GrpcGenerated 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 55e2cd81530..6a7b93baf06 100644 --- a/core/src/main/java/io/grpc/internal/GrpcUtil.java +++ b/core/src/main/java/io/grpc/internal/GrpcUtil.java @@ -205,7 +205,7 @@ public byte[] parseAsciiString(byte[] serialized) { public static final Splitter ACCEPT_ENCODING_SPLITTER = Splitter.on(',').trimResults(); - private static final String IMPLEMENTATION_VERSION = "1.42.0-SNAPSHOT"; // CURRENT_GRPC_VERSION + private static final String IMPLEMENTATION_VERSION = "1.43.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 99ac5e5db1c..fbbcdd9b364 100644 --- a/examples/android/clientcache/app/build.gradle +++ b/examples/android/clientcache/app/build.gradle @@ -34,7 +34,7 @@ android { protobuf { protoc { artifact = 'com.google.protobuf:protoc:3.17.2' } plugins { - grpc { artifact = 'io.grpc:protoc-gen-grpc-java:1.42.0-SNAPSHOT' // CURRENT_GRPC_VERSION + grpc { artifact = 'io.grpc:protoc-gen-grpc-java:1.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION } } generateProtoTasks { @@ -54,12 +54,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.42.0-SNAPSHOT' // CURRENT_GRPC_VERSION - implementation 'io.grpc:grpc-protobuf-lite:1.42.0-SNAPSHOT' // CURRENT_GRPC_VERSION - implementation 'io.grpc:grpc-stub:1.42.0-SNAPSHOT' // CURRENT_GRPC_VERSION + implementation 'io.grpc:grpc-okhttp:1.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION + implementation 'io.grpc:grpc-protobuf-lite:1.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION + implementation 'io.grpc:grpc-stub:1.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION implementation 'org.apache.tomcat:annotations-api:6.0.53' testImplementation 'junit:junit:4.12' testImplementation 'com.google.truth:truth:1.0.1' - testImplementation 'io.grpc:grpc-testing:1.42.0-SNAPSHOT' // CURRENT_GRPC_VERSION + testImplementation 'io.grpc:grpc-testing:1.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION } diff --git a/examples/android/helloworld/app/build.gradle b/examples/android/helloworld/app/build.gradle index 29b7bc61c0f..0c71d91c0d2 100644 --- a/examples/android/helloworld/app/build.gradle +++ b/examples/android/helloworld/app/build.gradle @@ -32,7 +32,7 @@ android { protobuf { protoc { artifact = 'com.google.protobuf:protoc:3.17.2' } plugins { - grpc { artifact = 'io.grpc:protoc-gen-grpc-java:1.42.0-SNAPSHOT' // CURRENT_GRPC_VERSION + grpc { artifact = 'io.grpc:protoc-gen-grpc-java:1.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION } } generateProtoTasks { @@ -52,8 +52,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.42.0-SNAPSHOT' // CURRENT_GRPC_VERSION - implementation 'io.grpc:grpc-protobuf-lite:1.42.0-SNAPSHOT' // CURRENT_GRPC_VERSION - implementation 'io.grpc:grpc-stub:1.42.0-SNAPSHOT' // CURRENT_GRPC_VERSION + implementation 'io.grpc:grpc-okhttp:1.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION + implementation 'io.grpc:grpc-protobuf-lite:1.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION + implementation 'io.grpc:grpc-stub:1.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION implementation 'org.apache.tomcat:annotations-api:6.0.53' } diff --git a/examples/android/routeguide/app/build.gradle b/examples/android/routeguide/app/build.gradle index 665730306a9..09dc587850f 100644 --- a/examples/android/routeguide/app/build.gradle +++ b/examples/android/routeguide/app/build.gradle @@ -32,7 +32,7 @@ android { protobuf { protoc { artifact = 'com.google.protobuf:protoc:3.17.2' } plugins { - grpc { artifact = 'io.grpc:protoc-gen-grpc-java:1.42.0-SNAPSHOT' // CURRENT_GRPC_VERSION + grpc { artifact = 'io.grpc:protoc-gen-grpc-java:1.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION } } generateProtoTasks { @@ -52,8 +52,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.42.0-SNAPSHOT' // CURRENT_GRPC_VERSION - implementation 'io.grpc:grpc-protobuf-lite:1.42.0-SNAPSHOT' // CURRENT_GRPC_VERSION - implementation 'io.grpc:grpc-stub:1.42.0-SNAPSHOT' // CURRENT_GRPC_VERSION + implementation 'io.grpc:grpc-okhttp:1.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION + implementation 'io.grpc:grpc-protobuf-lite:1.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION + implementation 'io.grpc:grpc-stub:1.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION implementation 'org.apache.tomcat:annotations-api:6.0.53' } diff --git a/examples/android/strictmode/app/build.gradle b/examples/android/strictmode/app/build.gradle index 98374f10b4e..9fb69f3f690 100644 --- a/examples/android/strictmode/app/build.gradle +++ b/examples/android/strictmode/app/build.gradle @@ -33,7 +33,7 @@ android { protobuf { protoc { artifact = 'com.google.protobuf:protoc:3.17.2' } plugins { - grpc { artifact = 'io.grpc:protoc-gen-grpc-java:1.42.0-SNAPSHOT' // CURRENT_GRPC_VERSION + grpc { artifact = 'io.grpc:protoc-gen-grpc-java:1.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION } } generateProtoTasks { @@ -53,8 +53,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.42.0-SNAPSHOT' // CURRENT_GRPC_VERSION - implementation 'io.grpc:grpc-protobuf-lite:1.42.0-SNAPSHOT' // CURRENT_GRPC_VERSION - implementation 'io.grpc:grpc-stub:1.42.0-SNAPSHOT' // CURRENT_GRPC_VERSION + implementation 'io.grpc:grpc-okhttp:1.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION + implementation 'io.grpc:grpc-protobuf-lite:1.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION + implementation 'io.grpc:grpc-stub:1.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION implementation 'org.apache.tomcat:annotations-api:6.0.53' } diff --git a/examples/build.gradle b/examples/build.gradle index db8d9b77c17..4c88b826d38 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.42.0-SNAPSHOT' // CURRENT_GRPC_VERSION +def grpcVersion = '1.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION def protobufVersion = '3.17.2' def protocVersion = protobufVersion diff --git a/examples/example-alts/build.gradle b/examples/example-alts/build.gradle index 2925085f57f..a0d726f6768 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.42.0-SNAPSHOT' // CURRENT_GRPC_VERSION +def grpcVersion = '1.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION def protocVersion = '3.17.2' dependencies { diff --git a/examples/example-gauth/build.gradle b/examples/example-gauth/build.gradle index bef50494821..2558469feb3 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.42.0-SNAPSHOT' // CURRENT_GRPC_VERSION +def grpcVersion = '1.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION def protobufVersion = '3.17.2' def protocVersion = protobufVersion diff --git a/examples/example-gauth/pom.xml b/examples/example-gauth/pom.xml index ba08911a924..67db6c7c9fb 100644 --- a/examples/example-gauth/pom.xml +++ b/examples/example-gauth/pom.xml @@ -6,13 +6,13 @@ jar - 1.42.0-SNAPSHOT + 1.43.0-SNAPSHOT example-gauth https://github.com/grpc/grpc-java UTF-8 - 1.42.0-SNAPSHOT + 1.43.0-SNAPSHOT 3.17.2 1.7 diff --git a/examples/example-hostname/build.gradle b/examples/example-hostname/build.gradle index f6e050d0570..331d8309136 100644 --- a/examples/example-hostname/build.gradle +++ b/examples/example-hostname/build.gradle @@ -21,7 +21,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.42.0-SNAPSHOT' // CURRENT_GRPC_VERSION +def grpcVersion = '1.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION def protobufVersion = '3.17.2' dependencies { diff --git a/examples/example-hostname/pom.xml b/examples/example-hostname/pom.xml index 0a22488d3e3..d4625193bf8 100644 --- a/examples/example-hostname/pom.xml +++ b/examples/example-hostname/pom.xml @@ -6,13 +6,13 @@ jar - 1.42.0-SNAPSHOT + 1.43.0-SNAPSHOT example-hostname https://github.com/grpc/grpc-java UTF-8 - 1.42.0-SNAPSHOT + 1.43.0-SNAPSHOT 3.17.2 1.7 diff --git a/examples/example-jwt-auth/build.gradle b/examples/example-jwt-auth/build.gradle index 851fa5ce095..94ae0610a21 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.42.0-SNAPSHOT' // CURRENT_GRPC_VERSION +def grpcVersion = '1.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION def protobufVersion = '3.17.2' def protocVersion = protobufVersion diff --git a/examples/example-jwt-auth/pom.xml b/examples/example-jwt-auth/pom.xml index 019d0cf4131..9ac3aefe34e 100644 --- a/examples/example-jwt-auth/pom.xml +++ b/examples/example-jwt-auth/pom.xml @@ -7,13 +7,13 @@ jar - 1.42.0-SNAPSHOT + 1.43.0-SNAPSHOT example-jwt-auth https://github.com/grpc/grpc-java UTF-8 - 1.42.0-SNAPSHOT + 1.43.0-SNAPSHOT 3.17.2 3.17.2 diff --git a/examples/example-tls/build.gradle b/examples/example-tls/build.gradle index a1696cfabdd..8a8ceb371af 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.42.0-SNAPSHOT' // CURRENT_GRPC_VERSION +def grpcVersion = '1.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION def protocVersion = '3.17.2' dependencies { diff --git a/examples/example-tls/pom.xml b/examples/example-tls/pom.xml index 4a1dfe1be15..60d632bbb87 100644 --- a/examples/example-tls/pom.xml +++ b/examples/example-tls/pom.xml @@ -6,13 +6,13 @@ jar - 1.42.0-SNAPSHOT + 1.43.0-SNAPSHOT example-tls https://github.com/grpc/grpc-java UTF-8 - 1.42.0-SNAPSHOT + 1.43.0-SNAPSHOT 3.17.2 2.0.34.Final diff --git a/examples/example-xds/build.gradle b/examples/example-xds/build.gradle index 9b5a5ee745f..d00beaa06bb 100644 --- a/examples/example-xds/build.gradle +++ b/examples/example-xds/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.42.0-SNAPSHOT' // CURRENT_GRPC_VERSION +def grpcVersion = '1.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION def nettyTcNativeVersion = '2.0.31.Final' def protocVersion = '3.17.2' diff --git a/examples/pom.xml b/examples/pom.xml index 93b9e502b90..0704ce80d88 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -6,13 +6,13 @@ jar - 1.42.0-SNAPSHOT + 1.43.0-SNAPSHOT examples https://github.com/grpc/grpc-java UTF-8 - 1.42.0-SNAPSHOT + 1.43.0-SNAPSHOT 3.17.2 3.17.2 From b86f4eba55f9114a05599b586456025b9903b359 Mon Sep 17 00:00:00 2001 From: yifeizhuang Date: Wed, 20 Oct 2021 11:13:57 -0700 Subject: [PATCH 02/50] xds: fix non permanent link to envoy rbac doc #8615 --- .../grpc/xds/internal/rbac/engine/GrpcAuthorizationEngine.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xds/src/main/java/io/grpc/xds/internal/rbac/engine/GrpcAuthorizationEngine.java b/xds/src/main/java/io/grpc/xds/internal/rbac/engine/GrpcAuthorizationEngine.java index 49574f9018c..ac7302fc38a 100644 --- a/xds/src/main/java/io/grpc/xds/internal/rbac/engine/GrpcAuthorizationEngine.java +++ b/xds/src/main/java/io/grpc/xds/internal/rbac/engine/GrpcAuthorizationEngine.java @@ -148,7 +148,7 @@ public abstract static class AuthenticatedMatcher implements Matcher { /** * Passing in null will match all authenticated user, i.e. SSL session is present. - * https://github.com/envoyproxy/envoy/blob/main/api/envoy/config/rbac/v3/rbac.proto#L240 + * https://github.com/envoyproxy/envoy/blob/3975bf5dadb43421907bbc52df57c0e8539c9a06/api/envoy/config/rbac/v3/rbac.proto#L253 * */ public static AuthenticatedMatcher create(@Nullable Matchers.StringMatcher delegate) { return new AutoValue_GrpcAuthorizationEngine_AuthenticatedMatcher(delegate); From 1fe62dd4176e8cc085d95c3bfe8ffe7ce78b2e03 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Wed, 20 Oct 2021 15:14:10 -0700 Subject: [PATCH 03/50] Fix sentences with a missing "be". (#8613) --- core/src/main/java/io/grpc/internal/AbstractClientStream.java | 2 +- core/src/main/java/io/grpc/internal/AbstractServerStream.java | 2 +- core/src/main/java/io/grpc/internal/AbstractStream.java | 2 +- netty/src/main/java/io/grpc/netty/NettyClientStream.java | 2 +- netty/src/main/java/io/grpc/netty/NettyServerStream.java | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/io/grpc/internal/AbstractClientStream.java b/core/src/main/java/io/grpc/internal/AbstractClientStream.java index bf032da3148..531206b29ca 100644 --- a/core/src/main/java/io/grpc/internal/AbstractClientStream.java +++ b/core/src/main/java/io/grpc/internal/AbstractClientStream.java @@ -217,7 +217,7 @@ protected TransportTracer getTransportTracer() { return transportTracer; } - /** This should only called from the transport thread. */ + /** This should only be called from the transport thread. */ protected abstract static class TransportState extends AbstractStream.TransportState { /** Whether listener.closed() has been called. */ private final StatsTraceContext statsTraceCtx; diff --git a/core/src/main/java/io/grpc/internal/AbstractServerStream.java b/core/src/main/java/io/grpc/internal/AbstractServerStream.java index 55724fdff88..3513ec9346f 100644 --- a/core/src/main/java/io/grpc/internal/AbstractServerStream.java +++ b/core/src/main/java/io/grpc/internal/AbstractServerStream.java @@ -171,7 +171,7 @@ public StatsTraceContext statsTraceContext() { } /** - * This should only called from the transport thread (except for private interactions with + * This should only be called from the transport thread (except for private interactions with * {@code AbstractServerStream}). */ protected abstract static class TransportState extends AbstractStream.TransportState { diff --git a/core/src/main/java/io/grpc/internal/AbstractStream.java b/core/src/main/java/io/grpc/internal/AbstractStream.java index 69df1eee8e4..3bcf70ac337 100644 --- a/core/src/main/java/io/grpc/internal/AbstractStream.java +++ b/core/src/main/java/io/grpc/internal/AbstractStream.java @@ -105,7 +105,7 @@ protected final void onSendingBytes(int numBytes) { } /** - * Stream state as used by the transport. This should only called from the transport thread + * Stream state as used by the transport. This should only be called from the transport thread * (except for private interactions with {@code AbstractStream}). */ public abstract static class TransportState diff --git a/netty/src/main/java/io/grpc/netty/NettyClientStream.java b/netty/src/main/java/io/grpc/netty/NettyClientStream.java index 3e044a39649..0e7e69635a8 100644 --- a/netty/src/main/java/io/grpc/netty/NettyClientStream.java +++ b/netty/src/main/java/io/grpc/netty/NettyClientStream.java @@ -219,7 +219,7 @@ public void cancel(Status status) { } } - /** This should only called from the transport thread. */ + /** This should only be called from the transport thread. */ public abstract static class TransportState extends Http2ClientStreamTransportState implements StreamIdHolder { private static final int NON_EXISTENT_ID = -1; diff --git a/netty/src/main/java/io/grpc/netty/NettyServerStream.java b/netty/src/main/java/io/grpc/netty/NettyServerStream.java index 7452b0ff5bf..3850a6a291c 100644 --- a/netty/src/main/java/io/grpc/netty/NettyServerStream.java +++ b/netty/src/main/java/io/grpc/netty/NettyServerStream.java @@ -164,7 +164,7 @@ public void cancel(Status status) { } } - /** This should only called from the transport thread. */ + /** This should only be called from the transport thread. */ public static class TransportState extends AbstractServerStream.TransportState implements StreamIdHolder { private final Http2Stream http2Stream; From 2e87cd6ae336547cea1c42bbe90f15ad648aa818 Mon Sep 17 00:00:00 2001 From: ZHANG Dapeng Date: Wed, 20 Oct 2021 17:56:53 -0700 Subject: [PATCH 04/50] Update README for Android API level (#8620) We dropped support for Android API levels <19 in #8583 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e6c06bde236..ea11cbde260 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ gRPC-Java - An RPC library and framework ======================================== gRPC-Java works with JDK 7. gRPC-Java clients are supported on Android API -levels 16 and up (Jelly Bean and later). Deploying gRPC servers on an Android +levels 19 and up (KitKat and later). Deploying gRPC servers on an Android device is not supported. TLS usage typically requires using Java 8, or Play Services Dynamic Security From 00bb28309089ae24be097cfa44392956b46e0efa Mon Sep 17 00:00:00 2001 From: ZHANG Dapeng Date: Wed, 20 Oct 2021 17:59:21 -0700 Subject: [PATCH 05/50] xds: add protection flag for federation (#8619) See https://github.com/grpc/proposal/pull/268/files#diff-e68147af61f13db5bd497e86ffd970fef6af29b88f4f23fb486deefdb35dfea3R659 for detail. --- .../java/io/grpc/xds/BootstrapperImpl.java | 8 +++++ .../io/grpc/xds/BootstrapperImplTest.java | 36 +++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/xds/src/main/java/io/grpc/xds/BootstrapperImpl.java b/xds/src/main/java/io/grpc/xds/BootstrapperImpl.java index 200ded3c1c4..c11dbbf7659 100644 --- a/xds/src/main/java/io/grpc/xds/BootstrapperImpl.java +++ b/xds/src/main/java/io/grpc/xds/BootstrapperImpl.java @@ -17,6 +17,7 @@ package io.grpc.xds; import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import io.grpc.ChannelCredentials; @@ -62,6 +63,10 @@ public class BootstrapperImpl extends Bootstrapper { @VisibleForTesting static final String CLIENT_FEATURE_DISABLE_OVERPROVISIONING = "envoy.lb.does_not_support_overprovisioning"; + @VisibleForTesting + static boolean enableFederation = + !Strings.isNullOrEmpty(System.getenv("GRPC_EXPERIMENTAL_XDS_FEDERATION")) + && Boolean.parseBoolean(System.getenv("GRPC_EXPERIMENTAL_XDS_FEDERATION")); private final XdsLogger logger; private FileReader reader = LocalFileReader.INSTANCE; @@ -199,6 +204,9 @@ BootstrapInfo bootstrap(Map rawData) throws XdsInitializationExceptio XdsLogLevel.INFO, "server_listener_resource_name_template: {0}", grpcServerResourceId); builder.serverListenerResourceNameTemplate(grpcServerResourceId); + if (!enableFederation) { + return builder.build(); + } String grpcClientDefaultListener = JsonUtil.getString(rawData, "client_default_listener_resource_name_template"); logger.log( diff --git a/xds/src/test/java/io/grpc/xds/BootstrapperImplTest.java b/xds/src/test/java/io/grpc/xds/BootstrapperImplTest.java index 183d67018dc..53b52a7bc02 100644 --- a/xds/src/test/java/io/grpc/xds/BootstrapperImplTest.java +++ b/xds/src/test/java/io/grpc/xds/BootstrapperImplTest.java @@ -57,6 +57,7 @@ public class BootstrapperImplTest { private String originalBootstrapPathFromSysProp; private String originalBootstrapConfigFromEnvVar; private String originalBootstrapConfigFromSysProp; + private boolean originalEnableFederation; @Before public void setUp() { @@ -69,6 +70,7 @@ private void saveEnvironment() { originalBootstrapPathFromSysProp = BootstrapperImpl.bootstrapPathFromSysProp; originalBootstrapConfigFromEnvVar = BootstrapperImpl.bootstrapConfigFromEnvVar; originalBootstrapConfigFromSysProp = BootstrapperImpl.bootstrapConfigFromSysProp; + originalEnableFederation = BootstrapperImpl.enableFederation; } @After @@ -77,6 +79,7 @@ public void restoreEnvironment() { BootstrapperImpl.bootstrapPathFromSysProp = originalBootstrapPathFromSysProp; BootstrapperImpl.bootstrapConfigFromEnvVar = originalBootstrapConfigFromEnvVar; BootstrapperImpl.bootstrapConfigFromSysProp = originalBootstrapConfigFromSysProp; + BootstrapperImpl.enableFederation = originalEnableFederation; } @Test @@ -680,6 +683,7 @@ public void fallbackToConfigFromSysProp() throws XdsInitializationException { @Test public void parseClientDefaultListenerResourceNameTemplate() throws Exception { + BootstrapperImpl.enableFederation = true; String rawData = "{\n" + " \"xds_servers\": [\n" + " ]\n" @@ -701,6 +705,7 @@ public void parseClientDefaultListenerResourceNameTemplate() throws Exception { @Test public void parseAuthorities() throws Exception { + BootstrapperImpl.enableFederation = true; String rawData = "{\n" + " \"xds_servers\": [\n" + " {\n" @@ -773,6 +778,37 @@ public void parseAuthorities() throws Exception { assertThat(authorityInfo.xdsServers().get(0).target()).isEqualTo("td2.googleapis.com:443"); } + @Test + public void badFederationConfig() throws Exception { + BootstrapperImpl.enableFederation = true; + String rawData = "{\n" + + " \"authorities\": {\n" + + " \"a.com\": {\n" + + " \"client_listener_resource_name_template\": \"xdstp://wrong/\"\n" + + " }\n" + + " },\n" + + " \"xds_servers\": [\n" + + " {\n" + + " \"server_uri\": \"" + SERVER_URI + "\",\n" + + " \"channel_creds\": [\n" + + " {\"type\": \"insecure\"}\n" + + " ]\n" + + " }\n" + + " ]\n" + + "}"; + bootstrapper.setFileReader(createFileReader(BOOTSTRAP_FILE_PATH, rawData)); + try { + bootstrapper.bootstrap(); + fail("should fail"); + } catch (XdsInitializationException e) { + assertThat(e).hasMessageThat().isEqualTo( + "client_listener_resource_name_template: 'xdstp://wrong/' does not start with " + + "xdstp://a.com/"); + } + BootstrapperImpl.enableFederation = false; + bootstrapper.bootstrap(); + } + private static BootstrapperImpl.FileReader createFileReader( final String expectedPath, final String rawData) { return new BootstrapperImpl.FileReader() { From 203515dd3d3dcc050a3343c6f26780b10dba91ea Mon Sep 17 00:00:00 2001 From: ZHANG Dapeng Date: Thu, 21 Oct 2021 21:24:51 -0700 Subject: [PATCH 06/50] rls: fix connectivity state aggregation (#8625) Fix connectivity state aggregation as per http://go/grpc-rls-lb-policy-design#heading=h.6e8tt7xcwcdn > Note that, for the purposes of aggregation, when a child policy reports TRANSIENT_FAILURE, we consider it to continue to be in that state until it reports READY (i.e., we ignore CONNECTING in between the two, no matter how many times it bounces back and forth between TRANSIENT_FAILURE and CONNECTING). --- .../io/grpc/rls/SubchannelStateManager.java | 2 ++ .../io/grpc/rls/SubchannelStateManagerImpl.java | 17 +++++++++++------ .../grpc/rls/ChildLoadBalancerHelperTest.java | 2 +- .../rls/SubchannelStateManagerImplTest.java | 8 ++++++++ 4 files changed, 22 insertions(+), 7 deletions(-) diff --git a/rls/src/main/java/io/grpc/rls/SubchannelStateManager.java b/rls/src/main/java/io/grpc/rls/SubchannelStateManager.java index f5437eac75a..041d2a817ab 100644 --- a/rls/src/main/java/io/grpc/rls/SubchannelStateManager.java +++ b/rls/src/main/java/io/grpc/rls/SubchannelStateManager.java @@ -16,6 +16,7 @@ package io.grpc.rls; +import com.google.common.annotations.VisibleForTesting; import io.grpc.ConnectivityState; import javax.annotation.Nullable; @@ -35,6 +36,7 @@ interface SubchannelStateManager { * {@code null}. */ @Nullable + @VisibleForTesting ConnectivityState getState(String name); /** Returns representative subchannel status from all registered subchannels. */ diff --git a/rls/src/main/java/io/grpc/rls/SubchannelStateManagerImpl.java b/rls/src/main/java/io/grpc/rls/SubchannelStateManagerImpl.java index 6d34e713f00..f2d6d612fd6 100644 --- a/rls/src/main/java/io/grpc/rls/SubchannelStateManagerImpl.java +++ b/rls/src/main/java/io/grpc/rls/SubchannelStateManagerImpl.java @@ -30,6 +30,7 @@ final class SubchannelStateManagerImpl implements SubchannelStateManager { private final HashMap stateMap = new HashMap<>(); private final Multiset stateMultiset = HashMultiset.create(); + private ConnectivityState currentState; @Override public void updateState(String name, ConnectivityState newState) { @@ -56,16 +57,20 @@ public ConnectivityState getState(String name) { @Override public ConnectivityState getAggregatedState() { if (stateMultiset.contains(ConnectivityState.READY)) { - return ConnectivityState.READY; + currentState = ConnectivityState.READY; } else if (stateMultiset.contains(ConnectivityState.CONNECTING)) { - return ConnectivityState.CONNECTING; + if (currentState != ConnectivityState.TRANSIENT_FAILURE) { + currentState = ConnectivityState.CONNECTING; + } } else if (stateMultiset.contains(ConnectivityState.IDLE)) { - return ConnectivityState.IDLE; + currentState = ConnectivityState.IDLE; } else if (stateMultiset.contains(ConnectivityState.TRANSIENT_FAILURE)) { - return ConnectivityState.TRANSIENT_FAILURE; + currentState = ConnectivityState.TRANSIENT_FAILURE; + } else { + // empty or shutdown + currentState = ConnectivityState.IDLE; } - // empty or shutdown - return ConnectivityState.IDLE; + return currentState; } @Override diff --git a/rls/src/test/java/io/grpc/rls/ChildLoadBalancerHelperTest.java b/rls/src/test/java/io/grpc/rls/ChildLoadBalancerHelperTest.java index 48901da3235..543540f498e 100644 --- a/rls/src/test/java/io/grpc/rls/ChildLoadBalancerHelperTest.java +++ b/rls/src/test/java/io/grpc/rls/ChildLoadBalancerHelperTest.java @@ -57,7 +57,7 @@ public void childLoadBalancerHelper_shouldReportsSubchannelState() { .isEqualTo(ConnectivityState.TRANSIENT_FAILURE); childLbHelper2.updateBalancingState(ConnectivityState.CONNECTING, picker2); - inOrder.verify(helper).updateBalancingState(ConnectivityState.CONNECTING, picker); + inOrder.verify(helper).updateBalancingState(ConnectivityState.TRANSIENT_FAILURE, picker); assertThat(subchannelStateManager.getState(target2)).isEqualTo(ConnectivityState.CONNECTING); childLbHelper1.updateBalancingState(ConnectivityState.READY, picker1); diff --git a/rls/src/test/java/io/grpc/rls/SubchannelStateManagerImplTest.java b/rls/src/test/java/io/grpc/rls/SubchannelStateManagerImplTest.java index a6d42e7d2a3..317d49448e5 100644 --- a/rls/src/test/java/io/grpc/rls/SubchannelStateManagerImplTest.java +++ b/rls/src/test/java/io/grpc/rls/SubchannelStateManagerImplTest.java @@ -90,5 +90,13 @@ public void getAggregateState_multipleSubchannels() { assertThat(subchannelStateManager.getAggregatedState()) .isEqualTo(ConnectivityState.TRANSIENT_FAILURE); + + subchannelStateManager.updateState("channel1", ConnectivityState.CONNECTING); + assertThat(subchannelStateManager.getAggregatedState()) + .isEqualTo(ConnectivityState.TRANSIENT_FAILURE); + + subchannelStateManager.updateState("channel1", ConnectivityState.READY); + assertThat(subchannelStateManager.getAggregatedState()) + .isEqualTo(ConnectivityState.READY); } } From 607362a7d2c9582df6d6b72c23e218cc676f0571 Mon Sep 17 00:00:00 2001 From: markb74 <57717302+markb74@users.noreply.github.com> Date: Mon, 25 Oct 2021 20:59:48 +0200 Subject: [PATCH 07/50] Add support for anonymous in-process servers. (#8589) Support anonymous in-process servers, and InProcessChannelBuilder.forTarget. Anonymous servers aren't registered statically, meaning they can't be looked up by name. Only the AnonymousInProcessSocketAddress passed to InProcessServerBuilder.forAddress(), (or subsequently fetched from Server.getListenSockets()) can be used to connect to the server. Supporting InProcessChannelBuilder.forTarget is particularly useful for production Android usage of in-process servers, where process startup latency is crucial. A custom name resolver can be used to create the server instance on demand without directly impacting the startup latency of in-process gRPC clients. Together, these features support a more-standard approach to "OnDeviceServer" referenced in gRFC L73. https://github.com/grpc/proposal/blob/master/L73-java-binderchannel.md#ondeviceserver --- .../AnonymousInProcessSocketAddress.java | 58 ++++++++++ .../inprocess/InProcessChannelBuilder.java | 41 ++++--- .../io/grpc/inprocess/InProcessServer.java | 49 +++++++-- .../inprocess/InProcessServerBuilder.java | 18 ++- .../io/grpc/inprocess/InProcessTransport.java | 29 ++--- .../AnonymousInProcessSocketAddressTest.java | 104 ++++++++++++++++++ .../AnonymousInProcessTransportTest.java | 57 ++++++++++ .../inprocess/InProcessTransportTest.java | 6 +- 8 files changed, 316 insertions(+), 46 deletions(-) create mode 100644 core/src/main/java/io/grpc/inprocess/AnonymousInProcessSocketAddress.java create mode 100644 core/src/test/java/io/grpc/inprocess/AnonymousInProcessSocketAddressTest.java create mode 100644 core/src/test/java/io/grpc/inprocess/AnonymousInProcessTransportTest.java diff --git a/core/src/main/java/io/grpc/inprocess/AnonymousInProcessSocketAddress.java b/core/src/main/java/io/grpc/inprocess/AnonymousInProcessSocketAddress.java new file mode 100644 index 00000000000..5f6486e335d --- /dev/null +++ b/core/src/main/java/io/grpc/inprocess/AnonymousInProcessSocketAddress.java @@ -0,0 +1,58 @@ +/* + * Copyright 2021 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.inprocess; + +import static com.google.common.base.Preconditions.checkState; + +import io.grpc.ExperimentalApi; +import java.io.IOException; +import java.net.SocketAddress; +import javax.annotation.Nullable; +import javax.annotation.concurrent.GuardedBy; + +/** + * Custom SocketAddress class for {@link InProcessTransport}, for + * a server which can only be referenced via this address instance. + */ +@ExperimentalApi("https://github.com/grpc/grpc-java/issues/8626") +public final class AnonymousInProcessSocketAddress extends SocketAddress { + private static final long serialVersionUID = -8567592561863414695L; + + @Nullable + @GuardedBy("this") + private InProcessServer server; + + /** Creates a new AnonymousInProcessSocketAddress. */ + public AnonymousInProcessSocketAddress() { } + + @Nullable + synchronized InProcessServer getServer() { + return server; + } + + synchronized void setServer(InProcessServer server) throws IOException { + if (this.server != null) { + throw new IOException("Server instance already registered"); + } + this.server = server; + } + + synchronized void clearServer(InProcessServer server) { + checkState(this.server == server); + this.server = null; + } +} diff --git a/core/src/main/java/io/grpc/inprocess/InProcessChannelBuilder.java b/core/src/main/java/io/grpc/inprocess/InProcessChannelBuilder.java index 8a309408a94..df396ae2f66 100644 --- a/core/src/main/java/io/grpc/inprocess/InProcessChannelBuilder.java +++ b/core/src/main/java/io/grpc/inprocess/InProcessChannelBuilder.java @@ -55,15 +55,28 @@ public final class InProcessChannelBuilder extends * @return a new builder */ public static InProcessChannelBuilder forName(String name) { - return new InProcessChannelBuilder(name); + return forAddress(new InProcessSocketAddress(checkNotNull(name, "name"))); } /** - * Always fails. Call {@link #forName} instead. + * Create a channel builder that will connect to the server referenced by the given target URI. + * Only intended for use with a custom name resolver. + * + * @param target the identity of the server to connect to + * @return a new builder */ - @DoNotCall("Unsupported. Use forName() instead") public static InProcessChannelBuilder forTarget(String target) { - throw new UnsupportedOperationException("call forName() instead"); + return new InProcessChannelBuilder(null, checkNotNull(target, "target")); + } + + /** + * Create a channel builder that will connect to the server referenced by the given address. + * + * @param address the address of the server to connect to + * @return a new builder + */ + public static InProcessChannelBuilder forAddress(SocketAddress address) { + return new InProcessChannelBuilder(checkNotNull(address, "address"), null); } /** @@ -75,13 +88,11 @@ public static InProcessChannelBuilder forAddress(String name, int port) { } private final ManagedChannelImplBuilder managedChannelImplBuilder; - private final String name; private ScheduledExecutorService scheduledExecutorService; private int maxInboundMetadataSize = Integer.MAX_VALUE; private boolean transportIncludeStatusCause = false; - private InProcessChannelBuilder(String name) { - this.name = checkNotNull(name, "name"); + private InProcessChannelBuilder(@Nullable SocketAddress directAddress, @Nullable String target) { final class InProcessChannelTransportFactoryBuilder implements ClientTransportFactoryBuilder { @Override @@ -90,8 +101,13 @@ public ClientTransportFactory buildClientTransportFactory() { } } - managedChannelImplBuilder = new ManagedChannelImplBuilder(new InProcessSocketAddress(name), - "localhost", new InProcessChannelTransportFactoryBuilder(), null); + if (directAddress != null) { + managedChannelImplBuilder = new ManagedChannelImplBuilder(directAddress, "localhost", + new InProcessChannelTransportFactoryBuilder(), null); + } else { + managedChannelImplBuilder = new ManagedChannelImplBuilder(target, + new InProcessChannelTransportFactoryBuilder(), null); + } // In-process transport should not record its traffic to the stats module. // https://github.com/grpc/grpc-java/issues/2284 @@ -204,7 +220,7 @@ public InProcessChannelBuilder propagateCauseWithStatus(boolean enable) { ClientTransportFactory buildTransportFactory() { return new InProcessClientTransportFactory( - name, scheduledExecutorService, maxInboundMetadataSize, transportIncludeStatusCause); + scheduledExecutorService, maxInboundMetadataSize, transportIncludeStatusCause); } void setStatsEnabled(boolean value) { @@ -215,7 +231,6 @@ void setStatsEnabled(boolean value) { * Creates InProcess transports. Exposed for internal use, as it should be private. */ static final class InProcessClientTransportFactory implements ClientTransportFactory { - private final String name; private final ScheduledExecutorService timerService; private final boolean useSharedTimer; private final int maxInboundMetadataSize; @@ -223,10 +238,8 @@ static final class InProcessClientTransportFactory implements ClientTransportFac private final boolean includeCauseWithStatus; private InProcessClientTransportFactory( - String name, @Nullable ScheduledExecutorService scheduledExecutorService, int maxInboundMetadataSize, boolean includeCauseWithStatus) { - this.name = name; useSharedTimer = scheduledExecutorService == null; timerService = useSharedTimer ? SharedResourceHolder.get(GrpcUtil.TIMER_SERVICE) : scheduledExecutorService; @@ -242,7 +255,7 @@ public ConnectionClientTransport newClientTransport( } // TODO(carl-mastrangelo): Pass channelLogger in. return new InProcessTransport( - name, maxInboundMetadataSize, options.getAuthority(), options.getUserAgent(), + addr, maxInboundMetadataSize, options.getAuthority(), options.getUserAgent(), options.getEagAttributes(), includeCauseWithStatus); } diff --git a/core/src/main/java/io/grpc/inprocess/InProcessServer.java b/core/src/main/java/io/grpc/inprocess/InProcessServer.java index 7922ebd21a1..ffaca78f397 100644 --- a/core/src/main/java/io/grpc/inprocess/InProcessServer.java +++ b/core/src/main/java/io/grpc/inprocess/InProcessServer.java @@ -40,11 +40,16 @@ final class InProcessServer implements InternalServer { private static final ConcurrentMap registry = new ConcurrentHashMap<>(); - static InProcessServer findServer(String name) { - return registry.get(name); + static InProcessServer findServer(SocketAddress addr) { + if (addr instanceof AnonymousInProcessSocketAddress) { + return ((AnonymousInProcessSocketAddress) addr).getServer(); + } else if (addr instanceof InProcessSocketAddress) { + return registry.get(((InProcessSocketAddress) addr).getName()); + } + return null; } - private final String name; + private final SocketAddress listenAddress; private final int maxInboundMetadataSize; private final List streamTracerFactories; private ServerListener listener; @@ -60,7 +65,7 @@ static InProcessServer findServer(String name) { InProcessServer( InProcessServerBuilder builder, List streamTracerFactories) { - this.name = builder.name; + this.listenAddress = builder.listenAddress; this.schedulerPool = builder.schedulerPool; this.maxInboundMetadataSize = builder.maxInboundMetadataSize; this.streamTracerFactories = @@ -72,14 +77,25 @@ public void start(ServerListener serverListener) throws IOException { this.listener = serverListener; this.scheduler = schedulerPool.getObject(); // Must be last, as channels can start connecting after this point. - if (registry.putIfAbsent(name, this) != null) { - throw new IOException("name already registered: " + name); + registerInstance(); + } + + private void registerInstance() throws IOException { + if (listenAddress instanceof AnonymousInProcessSocketAddress) { + ((AnonymousInProcessSocketAddress) listenAddress).setServer(this); + } else if (listenAddress instanceof InProcessSocketAddress) { + String name = ((InProcessSocketAddress) listenAddress).getName(); + if (registry.putIfAbsent(name, this) != null) { + throw new IOException("name already registered: " + name); + } + } else { + throw new AssertionError(); } } @Override public SocketAddress getListenSocketAddress() { - return new InProcessSocketAddress(name); + return listenAddress; } @Override @@ -99,9 +115,7 @@ public List> getListenSocketStatsList() { @Override public void shutdown() { - if (!registry.remove(name, this)) { - throw new AssertionError(); - } + unregisterInstance(); scheduler = schedulerPool.returnObject(scheduler); synchronized (this) { shutdown = true; @@ -109,9 +123,22 @@ public void shutdown() { } } + private void unregisterInstance() { + if (listenAddress instanceof AnonymousInProcessSocketAddress) { + ((AnonymousInProcessSocketAddress) listenAddress).clearServer(this); + } else if (listenAddress instanceof InProcessSocketAddress) { + String name = ((InProcessSocketAddress) listenAddress).getName(); + if (!registry.remove(name, this)) { + throw new AssertionError(); + } + } else { + throw new AssertionError(); + } + } + @Override public String toString() { - return MoreObjects.toStringHelper(this).add("name", name).toString(); + return MoreObjects.toStringHelper(this).add("listenAddress", listenAddress).toString(); } synchronized ServerTransportListener register(InProcessTransport transport) { diff --git a/core/src/main/java/io/grpc/inprocess/InProcessServerBuilder.java b/core/src/main/java/io/grpc/inprocess/InProcessServerBuilder.java index 6c68189fcc9..4c5d46dc54d 100644 --- a/core/src/main/java/io/grpc/inprocess/InProcessServerBuilder.java +++ b/core/src/main/java/io/grpc/inprocess/InProcessServerBuilder.java @@ -34,6 +34,7 @@ import io.grpc.internal.ServerImplBuilder.ClientTransportServersBuilder; import io.grpc.internal.SharedResourcePool; import java.io.File; +import java.net.SocketAddress; import java.util.List; import java.util.UUID; import java.util.concurrent.ScheduledExecutorService; @@ -81,7 +82,16 @@ public final class InProcessServerBuilder extends * @return a new builder */ public static InProcessServerBuilder forName(String name) { - return new InProcessServerBuilder(name); + return forAddress(new InProcessSocketAddress(checkNotNull(name, "name"))); + } + + /** + * Create a server builder which listens on the given address. + * @param listenAddress The SocketAddress this server will listen on. + * @return a new builder + */ + public static InProcessServerBuilder forAddress(SocketAddress listenAddress) { + return new InProcessServerBuilder(listenAddress); } /** @@ -100,13 +110,13 @@ public static String generateName() { } private final ServerImplBuilder serverImplBuilder; - final String name; + final SocketAddress listenAddress; int maxInboundMetadataSize = Integer.MAX_VALUE; ObjectPool schedulerPool = SharedResourcePool.forResource(GrpcUtil.TIMER_SERVICE); - private InProcessServerBuilder(String name) { - this.name = Preconditions.checkNotNull(name, "name"); + private InProcessServerBuilder(SocketAddress listenAddress) { + this.listenAddress = checkNotNull(listenAddress, "listenAddress"); final class InProcessClientTransportServersBuilder implements ClientTransportServersBuilder { @Override diff --git a/core/src/main/java/io/grpc/inprocess/InProcessTransport.java b/core/src/main/java/io/grpc/inprocess/InProcessTransport.java index 895b709559b..2f4870fdcc2 100644 --- a/core/src/main/java/io/grpc/inprocess/InProcessTransport.java +++ b/core/src/main/java/io/grpc/inprocess/InProcessTransport.java @@ -59,6 +59,7 @@ import io.grpc.internal.StatsTraceContext; import io.grpc.internal.StreamListener; import java.io.InputStream; +import java.net.SocketAddress; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collections; @@ -80,7 +81,7 @@ final class InProcessTransport implements ServerTransport, ConnectionClientTrans private static final Logger log = Logger.getLogger(InProcessTransport.class.getName()); private final InternalLogId logId; - private final String name; + private final SocketAddress address; private final int clientMaxInboundMetadataSize; private final String authority; private final String userAgent; @@ -119,10 +120,10 @@ protected void handleNotInUse() { } }; - private InProcessTransport(String name, int maxInboundMetadataSize, String authority, + private InProcessTransport(SocketAddress address, int maxInboundMetadataSize, String authority, String userAgent, Attributes eagAttrs, Optional optionalServerListener, boolean includeCauseWithStatus) { - this.name = name; + this.address = address; this.clientMaxInboundMetadataSize = maxInboundMetadataSize; this.authority = authority; this.userAgent = GrpcUtil.getGrpcUserAgent("inprocess", userAgent); @@ -130,18 +131,18 @@ private InProcessTransport(String name, int maxInboundMetadataSize, String autho this.attributes = Attributes.newBuilder() .set(GrpcAttributes.ATTR_SECURITY_LEVEL, SecurityLevel.PRIVACY_AND_INTEGRITY) .set(GrpcAttributes.ATTR_CLIENT_EAG_ATTRS, eagAttrs) - .set(Grpc.TRANSPORT_ATTR_REMOTE_ADDR, new InProcessSocketAddress(name)) - .set(Grpc.TRANSPORT_ATTR_LOCAL_ADDR, new InProcessSocketAddress(name)) + .set(Grpc.TRANSPORT_ATTR_REMOTE_ADDR, address) + .set(Grpc.TRANSPORT_ATTR_LOCAL_ADDR, address) .build(); this.optionalServerListener = optionalServerListener; - logId = InternalLogId.allocate(getClass(), name); + logId = InternalLogId.allocate(getClass(), address.toString()); this.includeCauseWithStatus = includeCauseWithStatus; } public InProcessTransport( - String name, int maxInboundMetadataSize, String authority, String userAgent, + SocketAddress address, int maxInboundMetadataSize, String authority, String userAgent, Attributes eagAttrs, boolean includeCauseWithStatus) { - this(name, maxInboundMetadataSize, authority, userAgent, eagAttrs, + this(address, maxInboundMetadataSize, authority, userAgent, eagAttrs, Optional.absent(), includeCauseWithStatus); } @@ -150,7 +151,7 @@ public InProcessTransport( Attributes eagAttrs, ObjectPool serverSchedulerPool, List serverStreamTracerFactories, ServerListener serverListener) { - this(name, maxInboundMetadataSize, authority, userAgent, eagAttrs, + this(new InProcessSocketAddress(name), maxInboundMetadataSize, authority, userAgent, eagAttrs, Optional.of(serverListener), false); this.serverMaxInboundMetadataSize = maxInboundMetadataSize; this.serverSchedulerPool = serverSchedulerPool; @@ -165,7 +166,7 @@ public synchronized Runnable start(ManagedClientTransport.Listener listener) { serverScheduler = serverSchedulerPool.getObject(); serverTransportListener = optionalServerListener.get().transportCreated(this); } else { - InProcessServer server = InProcessServer.findServer(name); + InProcessServer server = InProcessServer.findServer(address); if (server != null) { serverMaxInboundMetadataSize = server.getMaxInboundMetadataSize(); serverSchedulerPool = server.getScheduledExecutorServicePool(); @@ -176,7 +177,7 @@ public synchronized Runnable start(ManagedClientTransport.Listener listener) { } } if (serverTransportListener == null) { - shutdownStatus = Status.UNAVAILABLE.withDescription("Could not find server: " + name); + shutdownStatus = Status.UNAVAILABLE.withDescription("Could not find server: " + address); final Status localShutdownStatus = shutdownStatus; return new Runnable() { @Override @@ -194,8 +195,8 @@ public void run() { public void run() { synchronized (InProcessTransport.this) { Attributes serverTransportAttrs = Attributes.newBuilder() - .set(Grpc.TRANSPORT_ATTR_REMOTE_ADDR, new InProcessSocketAddress(name)) - .set(Grpc.TRANSPORT_ATTR_LOCAL_ADDR, new InProcessSocketAddress(name)) + .set(Grpc.TRANSPORT_ATTR_REMOTE_ADDR, address) + .set(Grpc.TRANSPORT_ATTR_LOCAL_ADDR, address) .build(); serverStreamAttributes = serverTransportListener.transportReady(serverTransportAttrs); clientTransportListener.transportReady(); @@ -307,7 +308,7 @@ public void shutdownNow(Status reason) { public String toString() { return MoreObjects.toStringHelper(this) .add("logId", logId.getId()) - .add("name", name) + .add("address", address) .toString(); } diff --git a/core/src/test/java/io/grpc/inprocess/AnonymousInProcessSocketAddressTest.java b/core/src/test/java/io/grpc/inprocess/AnonymousInProcessSocketAddressTest.java new file mode 100644 index 00000000000..9c1beb7dde3 --- /dev/null +++ b/core/src/test/java/io/grpc/inprocess/AnonymousInProcessSocketAddressTest.java @@ -0,0 +1,104 @@ +/* + * Copyright 2021 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.inprocess; + +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.fail; + +import com.google.common.testing.EqualsTester; +import io.grpc.ServerStreamTracer; +import java.io.IOException; +import java.util.Collections; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Unit tests for {@link AnonymousInProcessSocketAddress}. */ +@RunWith(JUnit4.class) +public class AnonymousInProcessSocketAddressTest { + + @Test + public void defaultState() { + AnonymousInProcessSocketAddress addr = new AnonymousInProcessSocketAddress(); + assertThat(addr.getServer()).isNull(); + } + + @Test + public void setServer() throws Exception { + AnonymousInProcessSocketAddress addr = new AnonymousInProcessSocketAddress(); + InProcessServer server = createAnonymousServer(); + addr.setServer(server); + assertThat(addr.getServer()).isSameInstanceAs(server); + } + + @Test + public void setServerTwice() throws Exception { + AnonymousInProcessSocketAddress addr = new AnonymousInProcessSocketAddress(); + InProcessServer server = createAnonymousServer(); + addr.setServer(server); + try { + addr.setServer(server); + fail("Expected IOException on attempt to set server twice"); + } catch (IOException ioe) { + // Expected. + } + } + + @Test + public void clearServer() throws Exception { + AnonymousInProcessSocketAddress addr = new AnonymousInProcessSocketAddress(); + InProcessServer server = createAnonymousServer(); + addr.setServer(server); + addr.clearServer(server); + assertThat(addr.getServer()).isNull(); + } + + @Test + public void clearServerWrongInstance() throws Exception { + AnonymousInProcessSocketAddress addr = new AnonymousInProcessSocketAddress(); + addr.setServer(createAnonymousServer()); + try { + addr.clearServer(createAnonymousServer()); + fail("Expected IllegalStateException on attempt to clear the wrong server"); + } catch (IllegalStateException ise) { + // Expected. + } + } + + @Test + public void equality() throws IOException { + AnonymousInProcessSocketAddress addrA = new AnonymousInProcessSocketAddress(); + AnonymousInProcessSocketAddress addrB = new AnonymousInProcessSocketAddress(); + AnonymousInProcessSocketAddress addrC = new AnonymousInProcessSocketAddress(); + InProcessServer server = createAnonymousServer(); + + // Ensure two addresses with the same server are still distinct from each other. + addrA.setServer(server); + addrB.setServer(server); + new EqualsTester() + .addEqualityGroup(addrA) + .addEqualityGroup(addrB) + .addEqualityGroup(addrC) + .testEquals(); + } + + private InProcessServer createAnonymousServer() { + AnonymousInProcessSocketAddress unused = new AnonymousInProcessSocketAddress(); + InProcessServerBuilder builder = InProcessServerBuilder.forAddress(unused); + return new InProcessServer(builder, Collections.emptyList()); + } +} diff --git a/core/src/test/java/io/grpc/inprocess/AnonymousInProcessTransportTest.java b/core/src/test/java/io/grpc/inprocess/AnonymousInProcessTransportTest.java new file mode 100644 index 00000000000..a78a604eac3 --- /dev/null +++ b/core/src/test/java/io/grpc/inprocess/AnonymousInProcessTransportTest.java @@ -0,0 +1,57 @@ +/* + * Copyright 2021 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.inprocess; + +import static com.google.common.truth.Truth.assertThat; + +import io.grpc.ServerStreamTracer; +import io.grpc.internal.GrpcUtil; +import io.grpc.internal.InternalServer; +import io.grpc.internal.ManagedClientTransport; +import java.util.List; +import org.junit.After; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Unit tests for {@link InProcessTransport} with an anonymous server. */ +@RunWith(JUnit4.class) +public final class AnonymousInProcessTransportTest extends InProcessTransportTest { + + private AnonymousInProcessSocketAddress address = new AnonymousInProcessSocketAddress(); + + @After + @Override + public void tearDown() throws InterruptedException { + super.tearDown(); + assertThat(address.getServer()).isNull(); + } + + @Override + protected InternalServer newServer( + List streamTracerFactories) { + InProcessServerBuilder builder = InProcessServerBuilder.forAddress(address) + .maxInboundMetadataSize(GrpcUtil.DEFAULT_MAX_HEADER_LIST_SIZE); + return new InProcessServer(builder, streamTracerFactories); + } + + @Override + protected ManagedClientTransport newClientTransport(InternalServer server) { + return new InProcessTransport( + address, GrpcUtil.DEFAULT_MAX_HEADER_LIST_SIZE, + testAuthority(server), USER_AGENT, eagAttrs(), false); + } +} diff --git a/core/src/test/java/io/grpc/inprocess/InProcessTransportTest.java b/core/src/test/java/io/grpc/inprocess/InProcessTransportTest.java index 7325cda73cc..9e63a3d9d7c 100644 --- a/core/src/test/java/io/grpc/inprocess/InProcessTransportTest.java +++ b/core/src/test/java/io/grpc/inprocess/InProcessTransportTest.java @@ -54,7 +54,7 @@ public class InProcessTransportTest extends AbstractTransportTest { private static final String TRANSPORT_NAME = "perfect-for-testing"; private static final String AUTHORITY = "a-testing-authority"; - private static final String USER_AGENT = "a-testing-user-agent"; + protected static final String USER_AGENT = "a-testing-user-agent"; @Rule public final GrpcCleanupRule grpcCleanupRule = new GrpcCleanupRule(); @@ -82,8 +82,8 @@ protected String testAuthority(InternalServer server) { @Override protected ManagedClientTransport newClientTransport(InternalServer server) { return new InProcessTransport( - TRANSPORT_NAME, GrpcUtil.DEFAULT_MAX_HEADER_LIST_SIZE, testAuthority(server), USER_AGENT, - eagAttrs(), false); + new InProcessSocketAddress(TRANSPORT_NAME), GrpcUtil.DEFAULT_MAX_HEADER_LIST_SIZE, + testAuthority(server), USER_AGENT, eagAttrs(), false); } @Override From e0ecd5cfde8ec228121ef7b2ffd56983059f25d7 Mon Sep 17 00:00:00 2001 From: Sergii Tkachenko Date: Mon, 25 Oct 2021 15:34:28 -0400 Subject: [PATCH 08/50] RELEASING.md: remove Travis; check milestones before the cut * Update RELEASING.md 1) Remove Travis Job 2) Add a note to check milestones before cutting the branch --- RELEASING.md | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/RELEASING.md b/RELEASING.md index 5c802f3eea5..f16cb37c3ad 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -61,7 +61,11 @@ convention of `v..x`, while the tags include the patch version `v..`. For example, the same branch `v1.7.x` would be used to create all `v1.7` tags (e.g. `v1.7.0`, `v1.7.1`). -1. For `master`, change root build files to the next minor snapshot (e.g. +1. Review the issues in the current release [milestone](https://github.com/grpc/grpc-java/milestones) + for issues that won't make the cut. Check if any of them can be + closed. Be aware of the issues with the 'release blocker' label. + Consider reaching out to the assignee for the status update. +2. For `master`, change root build files to the next minor snapshot (e.g. ``1.8.0-SNAPSHOT``). ```bash @@ -74,8 +78,8 @@ would be used to create all `v1.7` tags (e.g. `v1.7.0`, `v1.7.1`). $ ./gradlew build $ git commit -a -m "Start $MAJOR.$((MINOR+1)).0 development cycle" ``` -2. Go through PR review and submit. -3. Create the release branch starting just before your commit and push it to GitHub: +3. Go through PR review and submit. +4. Create the release branch starting just before your commit and push it to GitHub: ```bash $ git fetch upstream @@ -83,12 +87,6 @@ would be used to create all `v1.7` tags (e.g. `v1.7.0`, `v1.7.1`). $(git log --pretty=format:%H --grep "^Start $MAJOR.$((MINOR+1)).0 development cycle$" upstream/master)^ $ git push upstream v$MAJOR.$MINOR.x ``` -4. Ask a project admin to go to [Travis CI settings](https://travis-ci.org/grpc/grpc-java/settings) - and add a _Cron Job_: - * Branch: `v$MAJOR.$MINOR.x` - * Interval: `weekly` - * Options: `Do not run if there has been a build in the last 24h` - * Click _Add_ button 5. Continue with Google-internal steps at go/grpc/java/releasing. 6. Create a milestone for the next release. 7. Move items out of the release milestone that didn't make the cut. Issues that From f30d07dc2d8d7d54abb51ff7d4345081342ca47a Mon Sep 17 00:00:00 2001 From: ZHANG Dapeng Date: Wed, 27 Oct 2021 09:07:15 -0700 Subject: [PATCH 09/50] xds: add RlsClusterSpecifierPlugin for RLS-in-xDS (#8612) Add RlsClusterSpecifierPlugin as per the [design doc](http://go/grpc-rls-in-xds#heading=h.dmyrvi6ohebx) The structure of `ClusterSpecifierPlugin` is very similar to `io.grpc.xds.Filter`. The following changes to the existing code are made: - move `ConfigOrError` class out of `Filter` class to be shared with `ClusterSpecifierPlugin` - make `io.grpc.rls.RlsProtoData` public to be accessible by `io.grpc.xds` - treat empty defaultTarget in `io.grpc.rls.RlsProtoData.RouteLookupConfig` as null to support both json and proto config without defaultTarget field specified. --- .../main/java/io/grpc/rls/RlsProtoData.java | 33 ++-- xds/build.gradle | 1 + .../java/io/grpc/xds/ClientXdsClient.java | 1 - .../io/grpc/xds/ClusterSpecifierPlugin.java | 38 +++++ .../xds/ClusterSpecifierPluginRegistry.java | 54 +++++++ .../main/java/io/grpc/xds/ConfigOrError.java | 51 +++++++ xds/src/main/java/io/grpc/xds/Filter.java | 33 ---- ...teLookupServiceClusterSpecifierPlugin.java | 137 +++++++++++++++++ .../ClusterSpecifierPluginRegistryTest.java | 34 +++++ .../test/java/io/grpc/xds/RbacFilterTest.java | 1 - ...okupServiceClusterSpecifierPluginTest.java | 142 ++++++++++++++++++ 11 files changed, 479 insertions(+), 46 deletions(-) create mode 100644 xds/src/main/java/io/grpc/xds/ClusterSpecifierPlugin.java create mode 100644 xds/src/main/java/io/grpc/xds/ClusterSpecifierPluginRegistry.java create mode 100644 xds/src/main/java/io/grpc/xds/ConfigOrError.java create mode 100644 xds/src/main/java/io/grpc/xds/RouteLookupServiceClusterSpecifierPlugin.java create mode 100644 xds/src/test/java/io/grpc/xds/ClusterSpecifierPluginRegistryTest.java create mode 100644 xds/src/test/java/io/grpc/xds/RouteLookupServiceClusterSpecifierPluginTest.java diff --git a/rls/src/main/java/io/grpc/rls/RlsProtoData.java b/rls/src/main/java/io/grpc/rls/RlsProtoData.java index f13f0369d89..5192e9cf075 100644 --- a/rls/src/main/java/io/grpc/rls/RlsProtoData.java +++ b/rls/src/main/java/io/grpc/rls/RlsProtoData.java @@ -25,6 +25,7 @@ import com.google.common.base.Objects; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import io.grpc.Internal; import io.grpc.rls.RlsProtoData.GrpcKeyBuilder.Name; import java.util.HashSet; import java.util.List; @@ -35,7 +36,10 @@ import javax.annotation.concurrent.Immutable; /** RlsProtoData is a collection of internal representation of RouteLookupService proto messages. */ -final class RlsProtoData { +@Internal +public final class RlsProtoData { + + private RlsProtoData() {} /** A request object sent to route lookup service. */ @Immutable @@ -138,7 +142,7 @@ public String toString() { /** A config object for gRPC RouteLookupService. */ @Immutable - static final class RouteLookupConfig { + public static final class RouteLookupConfig { private static final long MAX_AGE_MILLIS = TimeUnit.MINUTES.toMillis(5); private static final long MAX_CACHE_SIZE = 5 * 1024 * 1024; @@ -160,7 +164,8 @@ static final class RouteLookupConfig { @Nullable private final String defaultTarget; - RouteLookupConfig( + /** Constructor. */ + public RouteLookupConfig( List grpcKeyBuilders, String lookupService, long lookupServiceTimeoutInMillis, @@ -197,6 +202,9 @@ static final class RouteLookupConfig { checkArgument(cacheSizeBytes > 0, "cacheSize must be positive"); this.cacheSizeBytes = Math.min(cacheSizeBytes, MAX_CACHE_SIZE); this.validTargets = ImmutableList.copyOf(checkNotNull(validTargets, "validTargets")); + if (defaultTarget != null && defaultTarget.isEmpty()) { + defaultTarget = null; + } this.defaultTarget = defaultTarget; } @@ -328,7 +336,7 @@ private static void checkUniqueName(List grpcKeyBuilders) { * is true, one of the specified names must be present for the keybuilder to match. */ @Immutable - static final class NameMatcher { + public static final class NameMatcher { private final String key; @@ -336,7 +344,8 @@ static final class NameMatcher { private final boolean optional; - NameMatcher(String key, List names, @Nullable Boolean optional) { + /** Constructor. */ + public NameMatcher(String key, List names, @Nullable Boolean optional) { this.key = checkNotNull(key, "key"); this.names = ImmutableList.copyOf(checkNotNull(names, "names")); this.optional = optional != null ? optional : true; @@ -389,7 +398,7 @@ public String toString() { } /** GrpcKeyBuilder is a configuration to construct headers consumed by route lookup service. */ - static final class GrpcKeyBuilder { + public static final class GrpcKeyBuilder { private final ImmutableList names; @@ -397,6 +406,7 @@ static final class GrpcKeyBuilder { private final ExtraKeys extraKeys; private final ImmutableMap constantKeys; + /** Constructor. All args should be nonnull. Headers should head unique keys. */ public GrpcKeyBuilder( List names, List headers, ExtraKeys extraKeys, Map constantKeys) { @@ -476,17 +486,18 @@ public String toString() { * required and includes the proto package name. The method name may be omitted, in which case * any method on the given service is matched. */ - static final class Name { + public static final class Name { private final String service; private final String method; - Name(String service) { + public Name(String service) { this(service, "*"); } - Name(String service, String method) { + /** The primary constructor. */ + public Name(String service, String method) { checkState( !checkNotNull(service, "service").isEmpty(), "service must not be empty or null"); @@ -531,7 +542,7 @@ public String toString() { } @AutoValue - abstract static class ExtraKeys { + public abstract static class ExtraKeys { static final ExtraKeys DEFAULT = create(null, null, null); @Nullable abstract String host(); @@ -540,7 +551,7 @@ abstract static class ExtraKeys { @Nullable abstract String method(); - static ExtraKeys create( + public static ExtraKeys create( @Nullable String host, @Nullable String service, @Nullable String method) { return new AutoValue_RlsProtoData_ExtraKeys(host, service, method); } diff --git a/xds/build.gradle b/xds/build.gradle index fdee8fab203..a8dbde3e0a1 100644 --- a/xds/build.gradle +++ b/xds/build.gradle @@ -39,6 +39,7 @@ dependencies { libraries.autovalue_annotation, libraries.opencensus_proto, libraries.protobuf_util + implementation project(path: ':grpc-rls') def nettyDependency = implementation project(':grpc-netty') testImplementation project(':grpc-core').sourceSets.test.output diff --git a/xds/src/main/java/io/grpc/xds/ClientXdsClient.java b/xds/src/main/java/io/grpc/xds/ClientXdsClient.java index 8a43717f97e..2a4405f45fc 100644 --- a/xds/src/main/java/io/grpc/xds/ClientXdsClient.java +++ b/xds/src/main/java/io/grpc/xds/ClientXdsClient.java @@ -73,7 +73,6 @@ import io.grpc.xds.EnvoyServerProtoData.FilterChainMatch; import io.grpc.xds.EnvoyServerProtoData.UpstreamTlsContext; import io.grpc.xds.Filter.ClientInterceptorBuilder; -import io.grpc.xds.Filter.ConfigOrError; import io.grpc.xds.Filter.FilterConfig; import io.grpc.xds.Filter.NamedFilterConfig; import io.grpc.xds.Filter.ServerInterceptorBuilder; diff --git a/xds/src/main/java/io/grpc/xds/ClusterSpecifierPlugin.java b/xds/src/main/java/io/grpc/xds/ClusterSpecifierPlugin.java new file mode 100644 index 00000000000..44de67b8295 --- /dev/null +++ b/xds/src/main/java/io/grpc/xds/ClusterSpecifierPlugin.java @@ -0,0 +1,38 @@ +/* + * Copyright 2021 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 com.google.protobuf.Message; + +/** + * Defines the parsing functionality of a ClusterSpecifierPlugin as defined in the Enovy proto + * api/envoy/config/route/v3/route.proto. + */ +interface ClusterSpecifierPlugin { + /** + * The proto message types supported by this plugin. A plugin will be registered by each of its + * supported message types. + */ + String[] typeUrls(); + + ConfigOrError parsePlugin(Message rawProtoMessage); + + /** Represents an opaque data structure holding configuration for a ClusterSpecifierPlugin. */ + interface PluginConfig { + String typeUrl(); + } +} diff --git a/xds/src/main/java/io/grpc/xds/ClusterSpecifierPluginRegistry.java b/xds/src/main/java/io/grpc/xds/ClusterSpecifierPluginRegistry.java new file mode 100644 index 00000000000..cf7b4cf523d --- /dev/null +++ b/xds/src/main/java/io/grpc/xds/ClusterSpecifierPluginRegistry.java @@ -0,0 +1,54 @@ +/* + * Copyright 2021 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 java.util.HashMap; +import java.util.Map; +import javax.annotation.Nullable; + +final class ClusterSpecifierPluginRegistry { + private static ClusterSpecifierPluginRegistry instance; + + private final Map supportedPlugins = new HashMap<>(); + + private ClusterSpecifierPluginRegistry() {} + + static synchronized ClusterSpecifierPluginRegistry getDefaultRegistry() { + if (instance == null) { + instance = newRegistry().register(RouteLookupServiceClusterSpecifierPlugin.INSTANCE); + } + return instance; + } + + private static ClusterSpecifierPluginRegistry newRegistry() { + return new ClusterSpecifierPluginRegistry(); + } + + private ClusterSpecifierPluginRegistry register(ClusterSpecifierPlugin... plugins) { + for (ClusterSpecifierPlugin plugin : plugins) { + for (String typeUrl : plugin.typeUrls()) { + supportedPlugins.put(typeUrl, plugin); + } + } + return this; + } + + @Nullable + ClusterSpecifierPlugin get(String typeUrl) { + return supportedPlugins.get(typeUrl); + } +} diff --git a/xds/src/main/java/io/grpc/xds/ConfigOrError.java b/xds/src/main/java/io/grpc/xds/ConfigOrError.java new file mode 100644 index 00000000000..e1235b5b544 --- /dev/null +++ b/xds/src/main/java/io/grpc/xds/ConfigOrError.java @@ -0,0 +1,51 @@ +/* + * Copyright 2021 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.base.Preconditions.checkNotNull; + +// TODO(zdapeng): Unify with ClientXdsClient.StructOrError, or just have parseFilterConfig() throw +// certain types of Exception. +final class ConfigOrError { + + /** + * Returns a {@link ConfigOrError} for the successfully converted data object. + */ + static ConfigOrError fromConfig(T config) { + return new ConfigOrError<>(config); + } + + /** + * Returns a {@link ConfigOrError} for the failure to convert the data object. + */ + static ConfigOrError fromError(String errorDetail) { + return new ConfigOrError<>(errorDetail); + } + + final String errorDetail; + final T config; + + private ConfigOrError(T config) { + this.config = checkNotNull(config, "config"); + this.errorDetail = null; + } + + private ConfigOrError(String errorDetail) { + this.config = null; + this.errorDetail = checkNotNull(errorDetail, "errorDetail"); + } +} diff --git a/xds/src/main/java/io/grpc/xds/Filter.java b/xds/src/main/java/io/grpc/xds/Filter.java index 3da71fd6a4c..4b2767687f3 100644 --- a/xds/src/main/java/io/grpc/xds/Filter.java +++ b/xds/src/main/java/io/grpc/xds/Filter.java @@ -16,8 +16,6 @@ package io.grpc.xds; -import static com.google.common.base.Preconditions.checkNotNull; - import com.google.common.base.MoreObjects; import com.google.protobuf.Message; import io.grpc.ClientInterceptor; @@ -72,37 +70,6 @@ ServerInterceptor buildServerInterceptor( FilterConfig config, @Nullable FilterConfig overrideConfig); } - // TODO(zdapeng): Unify with ClientXdsClient.StructOrError, or just have parseFilterConfig() throw - // certain types of Exception. - final class ConfigOrError { - /** - * Returns a {@link ConfigOrError} for the successfully converted data object. - */ - static ConfigOrError fromConfig(T config) { - return new ConfigOrError<>(config); - } - - /** - * Returns a {@link ConfigOrError} for the failure to convert the data object. - */ - static ConfigOrError fromError(String errorDetail) { - return new ConfigOrError<>(errorDetail); - } - - final String errorDetail; - final T config; - - private ConfigOrError(T config) { - this.config = checkNotNull(config, "config"); - this.errorDetail = null; - } - - private ConfigOrError(String errorDetail) { - this.config = null; - this.errorDetail = checkNotNull(errorDetail, "errorDetail"); - } - } - /** Filter config with instance name. */ final class NamedFilterConfig { // filter instance name diff --git a/xds/src/main/java/io/grpc/xds/RouteLookupServiceClusterSpecifierPlugin.java b/xds/src/main/java/io/grpc/xds/RouteLookupServiceClusterSpecifierPlugin.java new file mode 100644 index 00000000000..8bec286925f --- /dev/null +++ b/xds/src/main/java/io/grpc/xds/RouteLookupServiceClusterSpecifierPlugin.java @@ -0,0 +1,137 @@ +/* + * Copyright 2021 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 com.google.auto.value.AutoValue; +import com.google.protobuf.Any; +import com.google.protobuf.InvalidProtocolBufferException; +import com.google.protobuf.Message; +import com.google.protobuf.util.Durations; +import io.grpc.lookup.v1.GrpcKeyBuilder; +import io.grpc.lookup.v1.GrpcKeyBuilder.ExtraKeys; +import io.grpc.lookup.v1.GrpcKeyBuilder.Name; +import io.grpc.lookup.v1.NameMatcher; +import io.grpc.lookup.v1.RouteLookupConfig; +import io.grpc.rls.RlsProtoData; +import java.util.ArrayList; +import java.util.List; + +/** The ClusterSpecifierPlugin for RouteLookup policy. */ +final class RouteLookupServiceClusterSpecifierPlugin implements ClusterSpecifierPlugin { + + static final RouteLookupServiceClusterSpecifierPlugin INSTANCE = + new RouteLookupServiceClusterSpecifierPlugin(); + + private static final String TYPE_URL = + "type.googleapis.com/grpc.lookup.v1.RouteLookupConfig"; + + private RouteLookupServiceClusterSpecifierPlugin() {} + + @Override + public String[] typeUrls() { + return new String[] { + TYPE_URL, + }; + } + + @Override + public ConfigOrError parsePlugin(Message rawProtoMessage) { + if (!(rawProtoMessage instanceof Any)) { + return ConfigOrError.fromError("Invalid config type: " + rawProtoMessage.getClass()); + } + + Any anyMessage = (Any) rawProtoMessage; + RouteLookupConfig configProto; + try { + configProto = anyMessage.unpack(RouteLookupConfig.class); + } catch (InvalidProtocolBufferException e) { + return ConfigOrError.fromError("Invalid proto: " + e); + } + try { + List keyBuildersProto = configProto.getGrpcKeybuildersList(); + List keyBuilders = + new ArrayList<>(keyBuildersProto.size()); + for (GrpcKeyBuilder keyBuilderProto : keyBuildersProto) { + List namesProto = keyBuilderProto.getNamesList(); + List names = new ArrayList<>(namesProto.size()); + for (Name nameProto : namesProto) { + if (nameProto.getMethod().isEmpty()) { + names.add(new RlsProtoData.GrpcKeyBuilder.Name(nameProto.getService())); + } else { + names.add( + new RlsProtoData.GrpcKeyBuilder.Name( + nameProto.getService(), nameProto.getMethod())); + } + } + + List headersProto = keyBuilderProto.getHeadersList(); + List headers = new ArrayList<>(headersProto.size()); + for (NameMatcher headerProto : headersProto) { + headers.add( + new RlsProtoData.NameMatcher( + headerProto.getKey(), headerProto.getNamesList(), + headerProto.getRequiredMatch())); + } + + String host = null; + String service = null; + String method = null; + if (keyBuilderProto.hasExtraKeys()) { + ExtraKeys extraKeysProto = keyBuilderProto.getExtraKeys(); + host = extraKeysProto.getHost(); + service = extraKeysProto.getService(); + method = extraKeysProto.getMethod(); + } + RlsProtoData.ExtraKeys extraKeys = + RlsProtoData.ExtraKeys.create(host, service, method); + + RlsProtoData.GrpcKeyBuilder keyBuilder = + new RlsProtoData.GrpcKeyBuilder( + names, headers, extraKeys, keyBuilderProto.getConstantKeysMap()); + keyBuilders.add(keyBuilder); + } + RlsProtoData.RouteLookupConfig config = new RlsProtoData.RouteLookupConfig( + keyBuilders, + configProto.getLookupService(), + Durations.toMillis(configProto.getLookupServiceTimeout()), + configProto.hasMaxAge() ? Durations.toMillis(configProto.getMaxAge()) : null, + configProto.hasStaleAge() ? Durations.toMillis(configProto.getStaleAge()) : null, + configProto.getCacheSizeBytes(), + configProto.getValidTargetsList(), + configProto.getDefaultTarget()); + return ConfigOrError.fromConfig(RlsPluginConfig.create(config)); + } catch (RuntimeException e) { + return ConfigOrError.fromError( + "Error parsing RouteLookupConfig: \n" + configProto + "\n reason: " + e); + } + } + + @AutoValue + abstract static class RlsPluginConfig implements PluginConfig { + + abstract RlsProtoData.RouteLookupConfig config(); + + static RlsPluginConfig create(RlsProtoData.RouteLookupConfig config) { + return new AutoValue_RouteLookupServiceClusterSpecifierPlugin_RlsPluginConfig(config); + } + + @Override + public String typeUrl() { + return TYPE_URL; + } + } +} diff --git a/xds/src/test/java/io/grpc/xds/ClusterSpecifierPluginRegistryTest.java b/xds/src/test/java/io/grpc/xds/ClusterSpecifierPluginRegistryTest.java new file mode 100644 index 00000000000..7f04a2a7f39 --- /dev/null +++ b/xds/src/test/java/io/grpc/xds/ClusterSpecifierPluginRegistryTest.java @@ -0,0 +1,34 @@ +/* + * Copyright 2021 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 org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Tests for {@link ClusterSpecifierPluginRegistry}. */ +@RunWith(JUnit4.class) +public class ClusterSpecifierPluginRegistryTest { + @Test + public void pluginsInGlobaalInstance() { + assertThat(ClusterSpecifierPluginRegistry.getDefaultRegistry() + .get("type.googleapis.com/grpc.lookup.v1.RouteLookupConfig")) + .isEqualTo(RouteLookupServiceClusterSpecifierPlugin.INSTANCE); + } +} diff --git a/xds/src/test/java/io/grpc/xds/RbacFilterTest.java b/xds/src/test/java/io/grpc/xds/RbacFilterTest.java index da42ec1a2f6..f86b36df5e8 100644 --- a/xds/src/test/java/io/grpc/xds/RbacFilterTest.java +++ b/xds/src/test/java/io/grpc/xds/RbacFilterTest.java @@ -52,7 +52,6 @@ import io.grpc.ServerInterceptor; import io.grpc.Status; import io.grpc.testing.TestMethodDescriptors; -import io.grpc.xds.Filter.ConfigOrError; import io.grpc.xds.Filter.FilterConfig; import io.grpc.xds.internal.rbac.engine.GrpcAuthorizationEngine; import io.grpc.xds.internal.rbac.engine.GrpcAuthorizationEngine.AlwaysTrueMatcher; diff --git a/xds/src/test/java/io/grpc/xds/RouteLookupServiceClusterSpecifierPluginTest.java b/xds/src/test/java/io/grpc/xds/RouteLookupServiceClusterSpecifierPluginTest.java new file mode 100644 index 00000000000..1bfe4a26f24 --- /dev/null +++ b/xds/src/test/java/io/grpc/xds/RouteLookupServiceClusterSpecifierPluginTest.java @@ -0,0 +1,142 @@ +/* + * Copyright 2021 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 com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.protobuf.Any; +import com.google.protobuf.util.Durations; +import io.grpc.lookup.v1.GrpcKeyBuilder; +import io.grpc.lookup.v1.GrpcKeyBuilder.ExtraKeys; +import io.grpc.lookup.v1.GrpcKeyBuilder.Name; +import io.grpc.lookup.v1.NameMatcher; +import io.grpc.lookup.v1.RouteLookupConfig; +import io.grpc.rls.RlsProtoData; +import io.grpc.xds.RouteLookupServiceClusterSpecifierPlugin.RlsPluginConfig; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Tests for {@link RouteLookupServiceClusterSpecifierPlugin}. */ +@RunWith(JUnit4.class) +public class RouteLookupServiceClusterSpecifierPluginTest { + @Test + public void parseConfigWithAllFieldsGiven() { + RouteLookupConfig routeLookupConfig = RouteLookupConfig.newBuilder() + .addGrpcKeybuilders( + GrpcKeyBuilder.newBuilder() + .addNames(Name.newBuilder().setService("service1").setMethod("method1")) + .addNames(Name.newBuilder().setService("service2").setMethod("method2")) + .addHeaders( + NameMatcher.newBuilder().setKey("key1").addNames("v1").setRequiredMatch(true)) + .setExtraKeys( + ExtraKeys.newBuilder() + .setHost("host1").setService("service1").setMethod("method1")) + .putConstantKeys("key2", "value2")) + .setLookupService("rls-cbt.googleapis.com") + .setLookupServiceTimeout(Durations.fromMillis(1234)) + .setMaxAge(Durations.fromMillis(56789)) + .setStaleAge(Durations.fromMillis(1000)) + .setCacheSizeBytes(5000) + .addValidTargets("valid-target") + .setDefaultTarget("default-target") + .build(); + RlsPluginConfig config = + RouteLookupServiceClusterSpecifierPlugin.INSTANCE.parsePlugin(Any.pack(routeLookupConfig)) + .config; + assertThat(config.typeUrl()).isEqualTo("type.googleapis.com/grpc.lookup.v1.RouteLookupConfig"); + assertThat(config.config()).isEqualTo( + new RlsProtoData.RouteLookupConfig( + ImmutableList.of( + new RlsProtoData.GrpcKeyBuilder( + ImmutableList.of( + new RlsProtoData.GrpcKeyBuilder.Name("service1", "method1"), + new RlsProtoData.GrpcKeyBuilder.Name("service2", "method2")), + ImmutableList.of( + new RlsProtoData.NameMatcher("key1", ImmutableList.of("v1"), true)), + RlsProtoData.ExtraKeys.create("host1", "service1", "method1"), + ImmutableMap.of("key2", "value2") + )), + "rls-cbt.googleapis.com", + 1234, + 56789L, + 1000L, + 5000, + ImmutableList.of("valid-target"), + "default-target")); + } + + @Test + public void parseConfigWithOptionalFieldsUnspecified() { + RouteLookupConfig routeLookupConfig = RouteLookupConfig.newBuilder() + .addGrpcKeybuilders( + GrpcKeyBuilder.newBuilder() + .addNames(Name.newBuilder().setService("service1")) + .addNames(Name.newBuilder().setService("service2")) + .addHeaders( + NameMatcher.newBuilder().setKey("key1").addNames("v1").setRequiredMatch(true))) + .setLookupService("rls-cbt.googleapis.com") + .setLookupServiceTimeout(Durations.fromMillis(1234)) + .setCacheSizeBytes(5000) + .addValidTargets("valid-target") + .build(); + RlsPluginConfig config = + RouteLookupServiceClusterSpecifierPlugin.INSTANCE.parsePlugin(Any.pack(routeLookupConfig)) + .config; + assertThat(config.typeUrl()).isEqualTo("type.googleapis.com/grpc.lookup.v1.RouteLookupConfig"); + assertThat(config.config()).isEqualTo( + new RlsProtoData.RouteLookupConfig( + ImmutableList.of( + new RlsProtoData.GrpcKeyBuilder( + ImmutableList.of( + new RlsProtoData.GrpcKeyBuilder.Name("service1"), + new RlsProtoData.GrpcKeyBuilder.Name("service2")), + ImmutableList.of( + new RlsProtoData.NameMatcher("key1", ImmutableList.of("v1"), true)), + RlsProtoData.ExtraKeys.create(null, null, null), + ImmutableMap.of() + )), + "rls-cbt.googleapis.com", + 1234, + null, + null, + 5000, + ImmutableList.of("valid-target"), + null)); + } + + @Test + public void parseInvalidConfig() { + RouteLookupConfig routeLookupConfig = RouteLookupConfig.newBuilder() + .addGrpcKeybuilders( + GrpcKeyBuilder.newBuilder() + .addNames(Name.newBuilder().setService("service1")) + .addNames(Name.newBuilder().setService("service2")) + .addHeaders( + NameMatcher.newBuilder().setKey("key1").addNames("v1").setRequiredMatch(true))) + .setLookupService("rls-cbt.googleapis.com") + .setLookupServiceTimeout(Durations.fromMillis(1234)) + .setCacheSizeBytes(-5000) // negative + .addValidTargets("valid-target") + .build(); + ConfigOrError configOrError = + RouteLookupServiceClusterSpecifierPlugin.INSTANCE.parsePlugin(Any.pack(routeLookupConfig)); + assertThat(configOrError.errorDetail).contains("cacheSize must be positive"); + } +} From 997592192ba4423c7c93b35104499ad78580d9f7 Mon Sep 17 00:00:00 2001 From: markb74 <57717302+markb74@users.noreply.github.com> Date: Thu, 28 Oct 2021 11:30:43 +0200 Subject: [PATCH 10/50] binder: SecurityPolicy updates. (#8632) Add additional comments to the security policy class, to mention that implementing new policies requires significant care. Also add security policies which check the Sha256 of a peer apps's signature, so people can do trusted cross-app communication without having to implement their own policy. Finally, add the UntrustedSecurityPolicies class, since that's inevitably a policy you sometimes need as well. --- .../java/io/grpc/binder/SecurityPolicies.java | 164 ++++++++++++++++++ .../java/io/grpc/binder/SecurityPolicy.java | 5 + .../binder/UntrustedSecurityPolicies.java | 47 +++++ .../io/grpc/binder/SecurityPoliciesTest.java | 121 +++++++++++++ 4 files changed, 337 insertions(+) create mode 100644 binder/src/main/java/io/grpc/binder/UntrustedSecurityPolicies.java diff --git a/binder/src/main/java/io/grpc/binder/SecurityPolicies.java b/binder/src/main/java/io/grpc/binder/SecurityPolicies.java index be46b9e3e54..76bc2ab32ef 100644 --- a/binder/src/main/java/io/grpc/binder/SecurityPolicies.java +++ b/binder/src/main/java/io/grpc/binder/SecurityPolicies.java @@ -16,9 +16,21 @@ package io.grpc.binder; +import android.annotation.SuppressLint; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.Signature; +import android.os.Build; import android.os.Process; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import com.google.common.hash.Hashing; import io.grpc.ExperimentalApi; import io.grpc.Status; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; import javax.annotation.CheckReturnValue; /** Static factory methods for creating standard security policies. */ @@ -27,6 +39,7 @@ public final class SecurityPolicies { private static final int MY_UID = Process.myUid(); + private static final int SHA_256_BYTES_LENGTH = 32; private SecurityPolicies() {} @@ -55,4 +68,155 @@ public Status checkAuthorization(int uid) { } }; } + + /** + * Creates {@link SecurityPolicy} which checks if the SHA-256 hash of the package signature + * matches {@code requiredSignatureSha256Hash}. + * + * @param packageName the package name of the allowed package. + * @param requiredSignatureSha256Hash the SHA-256 digest of the signature of the allowed package. + * @throws NullPointerException if any of the inputs are {@code null}. + * @throws IllegalArgumentException if {@code requiredSignatureSha256Hash} is not of length 32. + */ + public static SecurityPolicy hasSignatureSha256Hash( + PackageManager packageManager, String packageName, byte[] requiredSignatureSha256Hash) { + return oneOfSignatureSha256Hash( + packageManager, packageName, ImmutableList.of(requiredSignatureSha256Hash)); + } + + /** + * Creates {@link SecurityPolicy} which checks if the SHA-256 hash of the package signature + * matches any of {@code requiredSignatureSha256Hashes}. + * + * @param packageName the package name of the allowed package. + * @param requiredSignatureSha256Hashes the SHA-256 digests of the signatures of the allowed + * package. + * @throws NullPointerException if any of the inputs are {@code null}. + * @throws IllegalArgumentException if {@code requiredSignatureSha256Hashes} is empty, or if any + * of the {@code requiredSignatureSha256Hashes} are not of length 32. + */ + public static SecurityPolicy oneOfSignatureSha256Hash( + PackageManager packageManager, + String packageName, + Collection requiredSignatureSha256Hashes) { + Preconditions.checkNotNull(packageManager, "packageManager"); + Preconditions.checkNotNull(packageName, "packageName"); + Preconditions.checkNotNull(requiredSignatureSha256Hashes, "requiredSignatureSha256Hashes"); + Preconditions.checkArgument(!requiredSignatureSha256Hashes.isEmpty(), + "requiredSignatureSha256Hashes"); + ImmutableList requiredSignatureSha256HashesImmutable = + ImmutableList.copyOf(requiredSignatureSha256Hashes); + + for (byte[] requiredSignatureSha256Hash : requiredSignatureSha256HashesImmutable) { + Preconditions.checkNotNull(requiredSignatureSha256Hash); + Preconditions.checkArgument(requiredSignatureSha256Hash.length == SHA_256_BYTES_LENGTH); + } + + return new SecurityPolicy() { + @Override + public Status checkAuthorization(int uid) { + return checkUidSignatureSha256( + packageManager, uid, packageName, requiredSignatureSha256HashesImmutable); + } + }; + } + + private static Status checkUidSignatureSha256( + PackageManager packageManager, + int uid, + String packageName, + ImmutableList requiredSignatureSha256Hashes) { + String[] packages = packageManager.getPackagesForUid(uid); + if (packages == null) { + return Status.UNAUTHENTICATED.withDescription( + "Rejected by (SHA-256 hash signature check) security policy"); + } + boolean packageNameMatched = false; + for (String pkg : packages) { + if (!packageName.equals(pkg)) { + continue; + } + packageNameMatched = true; + if (checkPackageSignatureSha256( + packageManager, + pkg, + requiredSignatureSha256Hashes)) { + return Status.OK; + } + } + return Status.PERMISSION_DENIED.withDescription( + "Rejected by (SHA-256 hash signature check) security policy. Package name matched: " + + packageNameMatched); + } + + /** + * Checks if the signature of {@code packageName} matches on of the given sha256 hashes. + * + * @param packageName the package to be checked + * @param requiredSignatureSha256Hashes a list of hashes. + * @return {@code true} if {@code packageName} has a signature matches one of the hashes. + */ + @SuppressWarnings("deprecation") // For PackageInfo.signatures + @SuppressLint("PackageManagerGetSignatures") // We only allow 1 signature. + private static boolean checkPackageSignatureSha256( + PackageManager packageManager, + String packageName, + ImmutableList requiredSignatureSha256Hashes) { + PackageInfo packageInfo; + try { + if (Build.VERSION.SDK_INT >= 28) { + packageInfo = + packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNING_CERTIFICATES); + if (packageInfo.signingInfo == null) { + return false; + } + Signature[] signatures = + packageInfo.signingInfo.hasMultipleSigners() + ? packageInfo.signingInfo.getApkContentsSigners() + : packageInfo.signingInfo.getSigningCertificateHistory(); + + for (Signature signature : signatures) { + if (checkSignatureSha256HashesMatch(signature, requiredSignatureSha256Hashes)) { + return true; + } + } + } else { + packageInfo = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES); + if (packageInfo.signatures == null || packageInfo.signatures.length != 1) { + // Reject multiply-signed apks because of b/13678484 + // (See PackageManagerGetSignatures supression above). + return false; + } + + if (checkSignatureSha256HashesMatch( + packageInfo.signatures[0], + requiredSignatureSha256Hashes)) { + return true; + } + } + } catch (NameNotFoundException nnfe) { + return false; + } + return false; + } + + /** + * Checks if the SHA-256 hash of the {@code signature} matches one of the {@code + * expectedSignatureSha256Hashes}. + */ + private static boolean checkSignatureSha256HashesMatch( + Signature signature, List expectedSignatureSha256Hashes) { + byte[] signatureHash = getSha256Hash(signature); + for (byte[] hash : expectedSignatureSha256Hashes) { + if (Arrays.equals(hash, signatureHash)) { + return true; + } + } + return false; + } + + /** Returns SHA-256 hash of the provided signature. */ + private static byte[] getSha256Hash(Signature signature) { + return Hashing.sha256().hashBytes(signature.toByteArray()).asBytes(); + } } diff --git a/binder/src/main/java/io/grpc/binder/SecurityPolicy.java b/binder/src/main/java/io/grpc/binder/SecurityPolicy.java index d7dad53fdc8..d13f3a863fd 100644 --- a/binder/src/main/java/io/grpc/binder/SecurityPolicy.java +++ b/binder/src/main/java/io/grpc/binder/SecurityPolicy.java @@ -23,6 +23,11 @@ /** * Decides whether a given Android UID is authorized to access some resource. * + * While it's possible to extend this class to define your own policy, it's strongly + * recommended that you only use the policies provided by the {@link SecurityPolicies} or + * {@link UntrustedSecurityPolicies} classes. Implementing your own security policy requires + * significant care, and an understanding of the details and pitfalls of Android security. + * *

IMPORTANT For any concrete extensions of this class, it's assumed that the * authorization status of a given UID will not change as long as a process with that UID is * alive. diff --git a/binder/src/main/java/io/grpc/binder/UntrustedSecurityPolicies.java b/binder/src/main/java/io/grpc/binder/UntrustedSecurityPolicies.java new file mode 100644 index 00000000000..7c842b025ac --- /dev/null +++ b/binder/src/main/java/io/grpc/binder/UntrustedSecurityPolicies.java @@ -0,0 +1,47 @@ +/* + * Copyright 2021 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.binder; + +import io.grpc.ExperimentalApi; +import io.grpc.Status; +import javax.annotation.CheckReturnValue; + +/** + * Static factory methods for creating untrusted security policies. + */ +@CheckReturnValue +@ExperimentalApi("https://github.com/grpc/grpc-java/issues/8022") +public final class UntrustedSecurityPolicies { + + private UntrustedSecurityPolicies() {} + + /** + * Return a security policy which allows any peer on device. + * Servers should only use this policy if they intend to expose + * a service to all applications on device. + * Clients should only use this policy if they don't need to trust the + * application they're connecting to. + */ + public static SecurityPolicy untrustedPublic() { + return new SecurityPolicy() { + @Override + public Status checkAuthorization(int uid) { + return Status.OK; + } + }; + } +} diff --git a/binder/src/test/java/io/grpc/binder/SecurityPoliciesTest.java b/binder/src/test/java/io/grpc/binder/SecurityPoliciesTest.java index 6fd9e22ebaa..b894b5bb4cd 100644 --- a/binder/src/test/java/io/grpc/binder/SecurityPoliciesTest.java +++ b/binder/src/test/java/io/grpc/binder/SecurityPoliciesTest.java @@ -17,22 +17,68 @@ package io.grpc.binder; import static com.google.common.truth.Truth.assertThat; +import static org.robolectric.Shadows.shadowOf; +import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.Signature; import android.os.Process; +import androidx.test.core.app.ApplicationProvider; +import com.google.common.collect.ImmutableList; +import com.google.common.hash.Hashing; import io.grpc.Status; +import io.grpc.binder.SecurityPolicy; +import java.util.HashSet; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; @RunWith(RobolectricTestRunner.class) public final class SecurityPoliciesTest { + private static final int MY_UID = Process.myUid(); private static final int OTHER_UID = MY_UID + 1; + private static final int OTHER_UID_SAME_SIGNATURE = MY_UID + 2; + private static final int OTHER_UID_NO_SIGNATURE = MY_UID + 3; private static final String PERMISSION_DENIED_REASONS = "some reasons"; + private static final String SIG1 = "1234"; + private static final String SIG2 = "4321"; + + private static final String OTHER_UID_PACKAGE_NAME = "other.package"; + private static final String OTHER_UID_SAME_SIGNATURE_PACKAGE_NAME = "other.package.samesignature"; + private static final String OTHER_UID_NO_SIGNATURE_PACKAGE_NAME = "other.package.nosignature"; + + private Context appContext; + private PackageManager packageManager; + private SecurityPolicy policy; + @Before + public void setUp() { + appContext = ApplicationProvider.getApplicationContext(); + packageManager = appContext.getPackageManager(); + installPackage(MY_UID, appContext.getPackageName(), SIG1); + installPackage(OTHER_UID, OTHER_UID_PACKAGE_NAME, SIG2); + installPackage(OTHER_UID_SAME_SIGNATURE, OTHER_UID_SAME_SIGNATURE_PACKAGE_NAME, SIG1); + installPackage(OTHER_UID_NO_SIGNATURE, OTHER_UID_NO_SIGNATURE_PACKAGE_NAME); + } + + @SuppressWarnings("deprecation") + private void installPackage(int uid, String packageName, String... signatures) { + PackageInfo info = new PackageInfo(); + info.packageName = packageName; + info.signatures = new Signature[signatures.length]; + for (int i = 0; i < signatures.length; i++) { + info.signatures[i] = new Signature(signatures[i]); + } + shadowOf(packageManager).installPackage(info); + shadowOf(packageManager).setPackagesForUid(uid, packageName); + } + @Test public void testInternalOnly() throws Exception { policy = SecurityPolicies.internalOnly(); @@ -53,4 +99,79 @@ public void testPermissionDenied() throws Exception { assertThat(policy.checkAuthorization(OTHER_UID).getDescription()) .isEqualTo(PERMISSION_DENIED_REASONS); } + + @Test + public void testHasSignatureSha256Hash_succeedsIfPackageNameAndSignatureHashMatch() + throws Exception { + policy = + SecurityPolicies.hasSignatureSha256Hash( + packageManager, OTHER_UID_PACKAGE_NAME, getSha256Hash(SIG2)); + + // THEN UID for package that has SIG2 will be authorized + assertThat(policy.checkAuthorization(OTHER_UID).getCode()).isEqualTo(Status.OK.getCode()); + } + + @Test + public void testHasSignatureSha256Hash_failsIfPackageNameDoesNotMatch() throws Exception { + policy = + SecurityPolicies.hasSignatureSha256Hash( + packageManager, appContext.getPackageName(), getSha256Hash(SIG1)); + + // THEN UID for package that has SIG1 but different package name will not be authorized + assertThat(policy.checkAuthorization(OTHER_UID_SAME_SIGNATURE).getCode()) + .isEqualTo(Status.PERMISSION_DENIED.getCode()); + } + + @Test + public void testHasSignatureSha256Hash_failsIfSignatureHashDoesNotMatch() throws Exception { + policy = + SecurityPolicies.hasSignatureSha256Hash( + packageManager, OTHER_UID_PACKAGE_NAME, getSha256Hash(SIG1)); + + // THEN UID for package that doesn't have SIG1 will not be authorized + assertThat(policy.checkAuthorization(OTHER_UID).getCode()) + .isEqualTo(Status.PERMISSION_DENIED.getCode()); + } + + @Test + public void testOneOfSignatureSha256Hash_succeedsIfPackageNameAndSignatureHashMatch() + throws Exception { + policy = + SecurityPolicies.oneOfSignatureSha256Hash( + packageManager, OTHER_UID_PACKAGE_NAME, ImmutableList.of(getSha256Hash(SIG2))); + + // THEN UID for package that has SIG2 will be authorized + assertThat(policy.checkAuthorization(OTHER_UID).getCode()).isEqualTo(Status.OK.getCode()); + } + + @Test + public void testOneOfSignatureSha256Hash_failsIfAllHashesDoNotMatch() throws Exception { + policy = + SecurityPolicies.oneOfSignatureSha256Hash( + packageManager, + appContext.getPackageName(), + ImmutableList.of(getSha256Hash(SIG1), getSha256Hash("1314"))); + + // THEN UID for package that has SIG1 but different package name will not be authorized + assertThat(policy.checkAuthorization(OTHER_UID_SAME_SIGNATURE).getCode()) + .isEqualTo(Status.PERMISSION_DENIED.getCode()); + } + + @Test + public void testOneOfSignatureSha256Hash_succeedsIfPackageNameAndOneOfSignatureHashesMatch() + throws Exception { + policy = + SecurityPolicies.oneOfSignatureSha256Hash( + packageManager, + OTHER_UID_PACKAGE_NAME, + ImmutableList.of(getSha256Hash(SIG1), getSha256Hash(SIG2))); + + // THEN UID for package that has SIG2 will be authorized + assertThat(policy.checkAuthorization(OTHER_UID).getCode()).isEqualTo(Status.OK.getCode()); + } + + private static byte[] getSha256Hash(String signatureString) { + return Hashing.sha256().hashBytes(new Signature(signatureString).toByteArray()).asBytes(); + } + } From ee395e0e43a6a1c85fefde733eede55114c5d2e7 Mon Sep 17 00:00:00 2001 From: Eric Anderson Date: Thu, 28 Oct 2021 12:55:47 -0700 Subject: [PATCH 11/50] Revert "binder: SecurityPolicy updates. (#8632)" This reverts commit 997592192ba4423c7c93b35104499ad78580d9f7. Hashing is a Beta API in Guava, so we can't use it as-is. --- .../java/io/grpc/binder/SecurityPolicies.java | 164 ------------------ .../java/io/grpc/binder/SecurityPolicy.java | 5 - .../binder/UntrustedSecurityPolicies.java | 47 ----- .../io/grpc/binder/SecurityPoliciesTest.java | 121 ------------- 4 files changed, 337 deletions(-) delete mode 100644 binder/src/main/java/io/grpc/binder/UntrustedSecurityPolicies.java diff --git a/binder/src/main/java/io/grpc/binder/SecurityPolicies.java b/binder/src/main/java/io/grpc/binder/SecurityPolicies.java index 76bc2ab32ef..be46b9e3e54 100644 --- a/binder/src/main/java/io/grpc/binder/SecurityPolicies.java +++ b/binder/src/main/java/io/grpc/binder/SecurityPolicies.java @@ -16,21 +16,9 @@ package io.grpc.binder; -import android.annotation.SuppressLint; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.content.pm.Signature; -import android.os.Build; import android.os.Process; -import com.google.common.base.Preconditions; -import com.google.common.collect.ImmutableList; -import com.google.common.hash.Hashing; import io.grpc.ExperimentalApi; import io.grpc.Status; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; import javax.annotation.CheckReturnValue; /** Static factory methods for creating standard security policies. */ @@ -39,7 +27,6 @@ public final class SecurityPolicies { private static final int MY_UID = Process.myUid(); - private static final int SHA_256_BYTES_LENGTH = 32; private SecurityPolicies() {} @@ -68,155 +55,4 @@ public Status checkAuthorization(int uid) { } }; } - - /** - * Creates {@link SecurityPolicy} which checks if the SHA-256 hash of the package signature - * matches {@code requiredSignatureSha256Hash}. - * - * @param packageName the package name of the allowed package. - * @param requiredSignatureSha256Hash the SHA-256 digest of the signature of the allowed package. - * @throws NullPointerException if any of the inputs are {@code null}. - * @throws IllegalArgumentException if {@code requiredSignatureSha256Hash} is not of length 32. - */ - public static SecurityPolicy hasSignatureSha256Hash( - PackageManager packageManager, String packageName, byte[] requiredSignatureSha256Hash) { - return oneOfSignatureSha256Hash( - packageManager, packageName, ImmutableList.of(requiredSignatureSha256Hash)); - } - - /** - * Creates {@link SecurityPolicy} which checks if the SHA-256 hash of the package signature - * matches any of {@code requiredSignatureSha256Hashes}. - * - * @param packageName the package name of the allowed package. - * @param requiredSignatureSha256Hashes the SHA-256 digests of the signatures of the allowed - * package. - * @throws NullPointerException if any of the inputs are {@code null}. - * @throws IllegalArgumentException if {@code requiredSignatureSha256Hashes} is empty, or if any - * of the {@code requiredSignatureSha256Hashes} are not of length 32. - */ - public static SecurityPolicy oneOfSignatureSha256Hash( - PackageManager packageManager, - String packageName, - Collection requiredSignatureSha256Hashes) { - Preconditions.checkNotNull(packageManager, "packageManager"); - Preconditions.checkNotNull(packageName, "packageName"); - Preconditions.checkNotNull(requiredSignatureSha256Hashes, "requiredSignatureSha256Hashes"); - Preconditions.checkArgument(!requiredSignatureSha256Hashes.isEmpty(), - "requiredSignatureSha256Hashes"); - ImmutableList requiredSignatureSha256HashesImmutable = - ImmutableList.copyOf(requiredSignatureSha256Hashes); - - for (byte[] requiredSignatureSha256Hash : requiredSignatureSha256HashesImmutable) { - Preconditions.checkNotNull(requiredSignatureSha256Hash); - Preconditions.checkArgument(requiredSignatureSha256Hash.length == SHA_256_BYTES_LENGTH); - } - - return new SecurityPolicy() { - @Override - public Status checkAuthorization(int uid) { - return checkUidSignatureSha256( - packageManager, uid, packageName, requiredSignatureSha256HashesImmutable); - } - }; - } - - private static Status checkUidSignatureSha256( - PackageManager packageManager, - int uid, - String packageName, - ImmutableList requiredSignatureSha256Hashes) { - String[] packages = packageManager.getPackagesForUid(uid); - if (packages == null) { - return Status.UNAUTHENTICATED.withDescription( - "Rejected by (SHA-256 hash signature check) security policy"); - } - boolean packageNameMatched = false; - for (String pkg : packages) { - if (!packageName.equals(pkg)) { - continue; - } - packageNameMatched = true; - if (checkPackageSignatureSha256( - packageManager, - pkg, - requiredSignatureSha256Hashes)) { - return Status.OK; - } - } - return Status.PERMISSION_DENIED.withDescription( - "Rejected by (SHA-256 hash signature check) security policy. Package name matched: " - + packageNameMatched); - } - - /** - * Checks if the signature of {@code packageName} matches on of the given sha256 hashes. - * - * @param packageName the package to be checked - * @param requiredSignatureSha256Hashes a list of hashes. - * @return {@code true} if {@code packageName} has a signature matches one of the hashes. - */ - @SuppressWarnings("deprecation") // For PackageInfo.signatures - @SuppressLint("PackageManagerGetSignatures") // We only allow 1 signature. - private static boolean checkPackageSignatureSha256( - PackageManager packageManager, - String packageName, - ImmutableList requiredSignatureSha256Hashes) { - PackageInfo packageInfo; - try { - if (Build.VERSION.SDK_INT >= 28) { - packageInfo = - packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNING_CERTIFICATES); - if (packageInfo.signingInfo == null) { - return false; - } - Signature[] signatures = - packageInfo.signingInfo.hasMultipleSigners() - ? packageInfo.signingInfo.getApkContentsSigners() - : packageInfo.signingInfo.getSigningCertificateHistory(); - - for (Signature signature : signatures) { - if (checkSignatureSha256HashesMatch(signature, requiredSignatureSha256Hashes)) { - return true; - } - } - } else { - packageInfo = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES); - if (packageInfo.signatures == null || packageInfo.signatures.length != 1) { - // Reject multiply-signed apks because of b/13678484 - // (See PackageManagerGetSignatures supression above). - return false; - } - - if (checkSignatureSha256HashesMatch( - packageInfo.signatures[0], - requiredSignatureSha256Hashes)) { - return true; - } - } - } catch (NameNotFoundException nnfe) { - return false; - } - return false; - } - - /** - * Checks if the SHA-256 hash of the {@code signature} matches one of the {@code - * expectedSignatureSha256Hashes}. - */ - private static boolean checkSignatureSha256HashesMatch( - Signature signature, List expectedSignatureSha256Hashes) { - byte[] signatureHash = getSha256Hash(signature); - for (byte[] hash : expectedSignatureSha256Hashes) { - if (Arrays.equals(hash, signatureHash)) { - return true; - } - } - return false; - } - - /** Returns SHA-256 hash of the provided signature. */ - private static byte[] getSha256Hash(Signature signature) { - return Hashing.sha256().hashBytes(signature.toByteArray()).asBytes(); - } } diff --git a/binder/src/main/java/io/grpc/binder/SecurityPolicy.java b/binder/src/main/java/io/grpc/binder/SecurityPolicy.java index d13f3a863fd..d7dad53fdc8 100644 --- a/binder/src/main/java/io/grpc/binder/SecurityPolicy.java +++ b/binder/src/main/java/io/grpc/binder/SecurityPolicy.java @@ -23,11 +23,6 @@ /** * Decides whether a given Android UID is authorized to access some resource. * - * While it's possible to extend this class to define your own policy, it's strongly - * recommended that you only use the policies provided by the {@link SecurityPolicies} or - * {@link UntrustedSecurityPolicies} classes. Implementing your own security policy requires - * significant care, and an understanding of the details and pitfalls of Android security. - * *

IMPORTANT For any concrete extensions of this class, it's assumed that the * authorization status of a given UID will not change as long as a process with that UID is * alive. diff --git a/binder/src/main/java/io/grpc/binder/UntrustedSecurityPolicies.java b/binder/src/main/java/io/grpc/binder/UntrustedSecurityPolicies.java deleted file mode 100644 index 7c842b025ac..00000000000 --- a/binder/src/main/java/io/grpc/binder/UntrustedSecurityPolicies.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2021 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.binder; - -import io.grpc.ExperimentalApi; -import io.grpc.Status; -import javax.annotation.CheckReturnValue; - -/** - * Static factory methods for creating untrusted security policies. - */ -@CheckReturnValue -@ExperimentalApi("https://github.com/grpc/grpc-java/issues/8022") -public final class UntrustedSecurityPolicies { - - private UntrustedSecurityPolicies() {} - - /** - * Return a security policy which allows any peer on device. - * Servers should only use this policy if they intend to expose - * a service to all applications on device. - * Clients should only use this policy if they don't need to trust the - * application they're connecting to. - */ - public static SecurityPolicy untrustedPublic() { - return new SecurityPolicy() { - @Override - public Status checkAuthorization(int uid) { - return Status.OK; - } - }; - } -} diff --git a/binder/src/test/java/io/grpc/binder/SecurityPoliciesTest.java b/binder/src/test/java/io/grpc/binder/SecurityPoliciesTest.java index b894b5bb4cd..6fd9e22ebaa 100644 --- a/binder/src/test/java/io/grpc/binder/SecurityPoliciesTest.java +++ b/binder/src/test/java/io/grpc/binder/SecurityPoliciesTest.java @@ -17,68 +17,22 @@ package io.grpc.binder; import static com.google.common.truth.Truth.assertThat; -import static org.robolectric.Shadows.shadowOf; -import android.content.Context; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.pm.Signature; import android.os.Process; -import androidx.test.core.app.ApplicationProvider; -import com.google.common.collect.ImmutableList; -import com.google.common.hash.Hashing; import io.grpc.Status; -import io.grpc.binder.SecurityPolicy; -import java.util.HashSet; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; @RunWith(RobolectricTestRunner.class) public final class SecurityPoliciesTest { - private static final int MY_UID = Process.myUid(); private static final int OTHER_UID = MY_UID + 1; - private static final int OTHER_UID_SAME_SIGNATURE = MY_UID + 2; - private static final int OTHER_UID_NO_SIGNATURE = MY_UID + 3; private static final String PERMISSION_DENIED_REASONS = "some reasons"; - private static final String SIG1 = "1234"; - private static final String SIG2 = "4321"; - - private static final String OTHER_UID_PACKAGE_NAME = "other.package"; - private static final String OTHER_UID_SAME_SIGNATURE_PACKAGE_NAME = "other.package.samesignature"; - private static final String OTHER_UID_NO_SIGNATURE_PACKAGE_NAME = "other.package.nosignature"; - - private Context appContext; - private PackageManager packageManager; - private SecurityPolicy policy; - @Before - public void setUp() { - appContext = ApplicationProvider.getApplicationContext(); - packageManager = appContext.getPackageManager(); - installPackage(MY_UID, appContext.getPackageName(), SIG1); - installPackage(OTHER_UID, OTHER_UID_PACKAGE_NAME, SIG2); - installPackage(OTHER_UID_SAME_SIGNATURE, OTHER_UID_SAME_SIGNATURE_PACKAGE_NAME, SIG1); - installPackage(OTHER_UID_NO_SIGNATURE, OTHER_UID_NO_SIGNATURE_PACKAGE_NAME); - } - - @SuppressWarnings("deprecation") - private void installPackage(int uid, String packageName, String... signatures) { - PackageInfo info = new PackageInfo(); - info.packageName = packageName; - info.signatures = new Signature[signatures.length]; - for (int i = 0; i < signatures.length; i++) { - info.signatures[i] = new Signature(signatures[i]); - } - shadowOf(packageManager).installPackage(info); - shadowOf(packageManager).setPackagesForUid(uid, packageName); - } - @Test public void testInternalOnly() throws Exception { policy = SecurityPolicies.internalOnly(); @@ -99,79 +53,4 @@ public void testPermissionDenied() throws Exception { assertThat(policy.checkAuthorization(OTHER_UID).getDescription()) .isEqualTo(PERMISSION_DENIED_REASONS); } - - @Test - public void testHasSignatureSha256Hash_succeedsIfPackageNameAndSignatureHashMatch() - throws Exception { - policy = - SecurityPolicies.hasSignatureSha256Hash( - packageManager, OTHER_UID_PACKAGE_NAME, getSha256Hash(SIG2)); - - // THEN UID for package that has SIG2 will be authorized - assertThat(policy.checkAuthorization(OTHER_UID).getCode()).isEqualTo(Status.OK.getCode()); - } - - @Test - public void testHasSignatureSha256Hash_failsIfPackageNameDoesNotMatch() throws Exception { - policy = - SecurityPolicies.hasSignatureSha256Hash( - packageManager, appContext.getPackageName(), getSha256Hash(SIG1)); - - // THEN UID for package that has SIG1 but different package name will not be authorized - assertThat(policy.checkAuthorization(OTHER_UID_SAME_SIGNATURE).getCode()) - .isEqualTo(Status.PERMISSION_DENIED.getCode()); - } - - @Test - public void testHasSignatureSha256Hash_failsIfSignatureHashDoesNotMatch() throws Exception { - policy = - SecurityPolicies.hasSignatureSha256Hash( - packageManager, OTHER_UID_PACKAGE_NAME, getSha256Hash(SIG1)); - - // THEN UID for package that doesn't have SIG1 will not be authorized - assertThat(policy.checkAuthorization(OTHER_UID).getCode()) - .isEqualTo(Status.PERMISSION_DENIED.getCode()); - } - - @Test - public void testOneOfSignatureSha256Hash_succeedsIfPackageNameAndSignatureHashMatch() - throws Exception { - policy = - SecurityPolicies.oneOfSignatureSha256Hash( - packageManager, OTHER_UID_PACKAGE_NAME, ImmutableList.of(getSha256Hash(SIG2))); - - // THEN UID for package that has SIG2 will be authorized - assertThat(policy.checkAuthorization(OTHER_UID).getCode()).isEqualTo(Status.OK.getCode()); - } - - @Test - public void testOneOfSignatureSha256Hash_failsIfAllHashesDoNotMatch() throws Exception { - policy = - SecurityPolicies.oneOfSignatureSha256Hash( - packageManager, - appContext.getPackageName(), - ImmutableList.of(getSha256Hash(SIG1), getSha256Hash("1314"))); - - // THEN UID for package that has SIG1 but different package name will not be authorized - assertThat(policy.checkAuthorization(OTHER_UID_SAME_SIGNATURE).getCode()) - .isEqualTo(Status.PERMISSION_DENIED.getCode()); - } - - @Test - public void testOneOfSignatureSha256Hash_succeedsIfPackageNameAndOneOfSignatureHashesMatch() - throws Exception { - policy = - SecurityPolicies.oneOfSignatureSha256Hash( - packageManager, - OTHER_UID_PACKAGE_NAME, - ImmutableList.of(getSha256Hash(SIG1), getSha256Hash(SIG2))); - - // THEN UID for package that has SIG2 will be authorized - assertThat(policy.checkAuthorization(OTHER_UID).getCode()).isEqualTo(Status.OK.getCode()); - } - - private static byte[] getSha256Hash(String signatureString) { - return Hashing.sha256().hashBytes(new Signature(signatureString).toByteArray()).asBytes(); - } - } From 602624887f92dd81ea0f1faf7c949d34255b9bc7 Mon Sep 17 00:00:00 2001 From: ZHANG Dapeng Date: Fri, 29 Oct 2021 10:12:38 -0700 Subject: [PATCH 12/50] rls: sync latest rls protos from grpc-proto (#8638) --- rls/src/main/proto/grpc/lookup/v1/rls.proto | 13 +++++-------- rls/src/main/proto/grpc/lookup/v1/rls_config.proto | 7 +++++++ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/rls/src/main/proto/grpc/lookup/v1/rls.proto b/rls/src/main/proto/grpc/lookup/v1/rls.proto index d9dd6c246f2..7d1735289d5 100644 --- a/rls/src/main/proto/grpc/lookup/v1/rls.proto +++ b/rls/src/main/proto/grpc/lookup/v1/rls.proto @@ -22,14 +22,6 @@ option java_package = "io.grpc.lookup.v1"; option java_outer_classname = "RlsProto"; message RouteLookupRequest { - // Full host name of the target server, e.g. firestore.googleapis.com. - // Only set for gRPC requests; HTTP requests must use key_map explicitly. - // Deprecated in favor of setting key_map keys with GrpcKeyBuilder.extra_keys. - string server = 1 [deprecated = true]; - // Full path of the request, i.e. "/service/method". - // Only set for gRPC requests; HTTP requests must use key_map explicitly. - // Deprecated in favor of setting key_map keys with GrpcKeyBuilder.extra_keys. - string path = 2 [deprecated = true]; // Target type allows the client to specify what kind of target format it // would like from RLS to allow it to find the regional server, e.g. "grpc". string target_type = 3; @@ -41,8 +33,13 @@ message RouteLookupRequest { } // Reason for making this request. Reason reason = 5; + // For REASON_STALE, the header_data from the stale response, if any. + string stale_header_data = 6; // Map of key values extracted via key builders for the gRPC or HTTP request. map key_map = 4; + + reserved 1, 2; + reserved "server", "path"; } message RouteLookupResponse { diff --git a/rls/src/main/proto/grpc/lookup/v1/rls_config.proto b/rls/src/main/proto/grpc/lookup/v1/rls_config.proto index db99a8949ea..9d2b6c54cfb 100644 --- a/rls/src/main/proto/grpc/lookup/v1/rls_config.proto +++ b/rls/src/main/proto/grpc/lookup/v1/rls_config.proto @@ -216,3 +216,10 @@ message RouteLookupConfig { reserved 10; reserved "request_processing_strategy"; } + +// RouteLookupClusterSpecifier is used in xDS to represent a cluster specifier +// plugin for RLS. +message RouteLookupClusterSpecifier { + // The RLS config for this cluster specifier plugin instance. + RouteLookupConfig route_lookup_config = 1; +} From 59c6b49fd4051b8d9d5cf958a762007a2f60280c Mon Sep 17 00:00:00 2001 From: ZHANG Dapeng Date: Fri, 29 Oct 2021 11:46:00 -0700 Subject: [PATCH 13/50] xds: lazily init MessagePrinter (#8639) Just for cleanup. The printer might be used in other class e.g. to convert RLS proto to string/Map. --- .../java/io/grpc/xds/AbstractXdsClient.java | 19 +++-- .../main/java/io/grpc/xds/MessagePrinter.java | 70 ++++++++++--------- .../java/io/grpc/xds/MessagePrinterTest.java | 9 ++- 3 files changed, 54 insertions(+), 44 deletions(-) diff --git a/xds/src/main/java/io/grpc/xds/AbstractXdsClient.java b/xds/src/main/java/io/grpc/xds/AbstractXdsClient.java index a6c3c2feb99..0e609ff7458 100644 --- a/xds/src/main/java/io/grpc/xds/AbstractXdsClient.java +++ b/xds/src/main/java/io/grpc/xds/AbstractXdsClient.java @@ -78,7 +78,6 @@ public void uncaughtException(Thread t, Throwable e) { throw new AssertionError(e); } }); - private final MessagePrinter msgPrinter = new MessagePrinter(); private final InternalLogId logId; private final XdsLogger logger; private final ManagedChannel channel; @@ -580,8 +579,9 @@ public void onNext(final io.envoyproxy.envoy.api.v2.DiscoveryResponse response) public void run() { ResourceType type = ResourceType.fromTypeUrl(response.getTypeUrl()); if (logger.isLoggable(XdsLogLevel.DEBUG)) { - logger.log(XdsLogLevel.DEBUG, "Received {0} response:\n{1}", - type, msgPrinter.print(response)); + logger.log( + XdsLogLevel.DEBUG, "Received {0} response:\n{1}", type, + MessagePrinter.print(response)); } handleRpcResponse(type, response.getVersionInfo(), response.getResourcesList(), response.getNonce()); @@ -633,7 +633,9 @@ void sendDiscoveryRequest(ResourceType type, String versionInfo, Collection Date: Mon, 1 Nov 2021 14:01:56 +0100 Subject: [PATCH 14/50] Support BinderChannelBuilder.forTarget. (#8633) Allows this class to be used with custom name resolvers. --- .../grpc/binder/BinderChannelSmokeTest.java | 14 ++- .../io/grpc/binder/BinderChannelBuilder.java | 56 ++++++++--- .../testing/FakeNameResolverProvider.java | 94 +++++++++++++++++++ 3 files changed, 151 insertions(+), 13 deletions(-) create mode 100644 testing/src/main/java/io/grpc/internal/testing/FakeNameResolverProvider.java diff --git a/binder/src/androidTest/java/io/grpc/binder/BinderChannelSmokeTest.java b/binder/src/androidTest/java/io/grpc/binder/BinderChannelSmokeTest.java index dd9cf26bd3d..41ea76146de 100644 --- a/binder/src/androidTest/java/io/grpc/binder/BinderChannelSmokeTest.java +++ b/binder/src/androidTest/java/io/grpc/binder/BinderChannelSmokeTest.java @@ -36,11 +36,13 @@ import io.grpc.ManagedChannel; import io.grpc.Metadata; import io.grpc.MethodDescriptor; +import io.grpc.NameResolverRegistry; import io.grpc.Server; import io.grpc.ServerCallHandler; import io.grpc.ServerInterceptors; import io.grpc.ServerServiceDefinition; import io.grpc.internal.GrpcUtil; +import io.grpc.internal.testing.FakeNameResolverProvider; import io.grpc.stub.ClientCalls; import io.grpc.stub.ServerCalls; import io.grpc.stub.StreamObserver; @@ -66,6 +68,7 @@ public final class BinderChannelSmokeTest { private static final int SLIGHTLY_MORE_THAN_ONE_BLOCK = 16 * 1024 + 100; private static final String MSG = "Some text which will be repeated many many times"; + private static final String SERVER_TARGET_URI = "fake://server"; final MethodDescriptor method = MethodDescriptor.newBuilder(StringMarshaller.INSTANCE, StringMarshaller.INSTANCE) @@ -85,7 +88,7 @@ public final class BinderChannelSmokeTest { .setType(MethodDescriptor.MethodType.BIDI_STREAMING) .build(); - AndroidComponentAddress serverAddress; + FakeNameResolverProvider fakeNameResolverProvider; ManagedChannel channel; AtomicReference headersCapture = new AtomicReference<>(); @@ -118,6 +121,8 @@ public void setUp() throws Exception { TestUtils.recordRequestHeadersInterceptor(headersCapture)); AndroidComponentAddress serverAddress = HostServices.allocateService(appContext); + fakeNameResolverProvider = new FakeNameResolverProvider(SERVER_TARGET_URI, serverAddress); + NameResolverRegistry.getDefaultRegistry().register(fakeNameResolverProvider); HostServices.configureService(serverAddress, HostServices.serviceParamsBuilder() .setServerFactory((service, receiver) -> @@ -132,6 +137,7 @@ public void setUp() throws Exception { @After public void tearDown() throws Exception { channel.shutdownNow(); + NameResolverRegistry.getDefaultRegistry().deregister(fakeNameResolverProvider); HostServices.awaitServiceShutdown(); } @@ -192,6 +198,12 @@ public void testStreamingCallOptionHeaders() throws Exception { assertThat(headersCapture.get().get(GrpcUtil.TIMEOUT_KEY)).isGreaterThan(0); } + @Test + public void testConnectViaTargetUri() throws Exception { + channel = BinderChannelBuilder.forTarget(SERVER_TARGET_URI, appContext).build(); + assertThat(doCall("Hello").get()).isEqualTo("Hello"); + } + private static String createLargeString(int size) { StringBuilder sb = new StringBuilder(); while (sb.length() < size) { diff --git a/binder/src/main/java/io/grpc/binder/BinderChannelBuilder.java b/binder/src/main/java/io/grpc/binder/BinderChannelBuilder.java index 99191cfad3c..91e4e8f1c76 100644 --- a/binder/src/main/java/io/grpc/binder/BinderChannelBuilder.java +++ b/binder/src/main/java/io/grpc/binder/BinderChannelBuilder.java @@ -67,13 +67,35 @@ public final class BinderChannelBuilder *

You the caller are responsible for managing the lifecycle of any channels built by the * resulting builder. They will not be shut down automatically. * - * @param targetAddress the {@link AndroidComponentAddress} referencing the service to bind to. + * @param directAddress the {@link AndroidComponentAddress} referencing the service to bind to. * @param sourceContext the context to bind from (e.g. The current Activity or Application). * @return a new builder */ public static BinderChannelBuilder forAddress( - AndroidComponentAddress targetAddress, Context sourceContext) { - return new BinderChannelBuilder(targetAddress, sourceContext); + AndroidComponentAddress directAddress, Context sourceContext) { + return new BinderChannelBuilder( + checkNotNull(directAddress, "directAddress"), null, sourceContext); + } + + /** + * Creates a channel builder that will bind to a remote Android service, via a string + * target name which will be resolved. + * + *

The underlying Android binding will be torn down when the channel becomes idle. This happens + * after 30 minutes without use by default but can be configured via {@link + * ManagedChannelBuilder#idleTimeout(long, TimeUnit)} or triggered manually with {@link + * ManagedChannel#enterIdle()}. + * + *

You the caller are responsible for managing the lifecycle of any channels built by the + * resulting builder. They will not be shut down automatically. + * + * @param target A target uri which should resolve into an {@link AndroidComponentAddress} + * referencing the service to bind to. + * @param sourceContext the context to bind from (e.g. The current Activity or Application). + * @return a new builder + */ + public static BinderChannelBuilder forTarget(String target, Context sourceContext) { + return new BinderChannelBuilder(null, checkNotNull(target, "target"), sourceContext); } /** @@ -88,7 +110,7 @@ public static BinderChannelBuilder forAddress(String name, int port) { /** * Always fails. Call {@link #forAddress(AndroidComponentAddress, Context)} instead. */ - @DoNotCall("Unsupported. Use forAddress(AndroidComponentAddress, Context) instead") + @DoNotCall("Unsupported. Use forTarget(String, Context) instead") public static BinderChannelBuilder forTarget(String target) { throw new UnsupportedOperationException( "call forAddress(AndroidComponentAddress, Context) instead"); @@ -104,9 +126,11 @@ public static BinderChannelBuilder forTarget(String target) { private BindServiceFlags bindServiceFlags; private BinderChannelBuilder( - AndroidComponentAddress targetAddress, + @Nullable AndroidComponentAddress directAddress, + @Nullable String target, Context sourceContext) { - mainThreadExecutor = ContextCompat.getMainExecutor(sourceContext); + mainThreadExecutor = + ContextCompat.getMainExecutor(checkNotNull(sourceContext, "sourceContext")); securityPolicy = SecurityPolicies.internalOnly(); inboundParcelablePolicy = InboundParcelablePolicy.DEFAULT; bindServiceFlags = BindServiceFlags.DEFAULTS; @@ -126,12 +150,20 @@ public ClientTransportFactory buildClientTransportFactory() { } } - managedChannelImplBuilder = - new ManagedChannelImplBuilder( - targetAddress, - targetAddress.getAuthority(), - new BinderChannelTransportFactoryBuilder(), - null); + if (directAddress != null) { + managedChannelImplBuilder = + new ManagedChannelImplBuilder( + directAddress, + directAddress.getAuthority(), + new BinderChannelTransportFactoryBuilder(), + null); + } else { + managedChannelImplBuilder = + new ManagedChannelImplBuilder( + target, + new BinderChannelTransportFactoryBuilder(), + null); + } } @Override diff --git a/testing/src/main/java/io/grpc/internal/testing/FakeNameResolverProvider.java b/testing/src/main/java/io/grpc/internal/testing/FakeNameResolverProvider.java new file mode 100644 index 00000000000..d056707b719 --- /dev/null +++ b/testing/src/main/java/io/grpc/internal/testing/FakeNameResolverProvider.java @@ -0,0 +1,94 @@ +/* + * Copyright 2021 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.internal.testing; + +import com.google.common.collect.ImmutableList; +import io.grpc.EquivalentAddressGroup; +import io.grpc.NameResolver; +import io.grpc.NameResolverProvider; +import io.grpc.Status; +import java.net.SocketAddress; +import java.net.URI; + +/** A name resolver to always resolve the given URI into the given address. */ +public final class FakeNameResolverProvider extends NameResolverProvider { + + private final URI targetUri; + private final SocketAddress address; + + public FakeNameResolverProvider(String targetUri, SocketAddress address) { + this.targetUri = URI.create(targetUri); + this.address = address; + } + + @Override + public NameResolver newNameResolver(URI targetUri, NameResolver.Args args) { + if (targetUri.equals(this.targetUri)) { + return new FakeNameResolver(address); + } + return null; + } + + @Override + protected boolean isAvailable() { + return true; + } + + @Override + protected int priority() { + return 5; // Default + } + + @Override + public String getDefaultScheme() { + return targetUri.getScheme(); + } + + /** A single name resolver. */ + private static final class FakeNameResolver extends NameResolver { + private static final String AUTHORITY = "fake-authority"; + + private final SocketAddress address; + private volatile boolean shutdown; + + private FakeNameResolver(SocketAddress address) { + this.address = address; + } + + @Override + public void start(Listener2 listener) { + if (shutdown) { + listener.onError(Status.FAILED_PRECONDITION.withDescription("Resolver is shutdown")); + } else { + listener.onResult( + ResolutionResult.newBuilder() + .setAddresses(ImmutableList.of(new EquivalentAddressGroup(address))) + .build()); + } + } + + @Override + public String getServiceAuthority() { + return AUTHORITY; + } + + @Override + public void shutdown() { + shutdown = true; + } + } +} From a46560e4fc9ec4771dc7824055d23f1301dc1e3d Mon Sep 17 00:00:00 2001 From: ZHANG Dapeng Date: Mon, 1 Nov 2021 09:44:58 -0700 Subject: [PATCH 15/50] xds: refactor XdsClient in preparation to support federation (#8630) See go/java-xds-client-api-for-federation for detailed description --- .../java/io/grpc/xds/AbstractXdsClient.java | 180 ++++------ .../java/io/grpc/xds/ClientXdsClient.java | 317 +++++++++++++----- .../java/io/grpc/xds/LoadReportClient.java | 6 +- .../grpc/xds/SharedXdsClientPoolProvider.java | 35 +- xds/src/main/java/io/grpc/xds/XdsClient.java | 44 +++ .../io/grpc/xds/ClientXdsClientTestBase.java | 11 +- .../xds/SharedXdsClientPoolProviderTest.java | 6 - 7 files changed, 363 insertions(+), 236 deletions(-) diff --git a/xds/src/main/java/io/grpc/xds/AbstractXdsClient.java b/xds/src/main/java/io/grpc/xds/AbstractXdsClient.java index 0e609ff7458..e29949eb8e5 100644 --- a/xds/src/main/java/io/grpc/xds/AbstractXdsClient.java +++ b/xds/src/main/java/io/grpc/xds/AbstractXdsClient.java @@ -28,6 +28,7 @@ import io.envoyproxy.envoy.service.discovery.v3.AggregatedDiscoveryServiceGrpc; import io.envoyproxy.envoy.service.discovery.v3.DiscoveryRequest; import io.envoyproxy.envoy.service.discovery.v3.DiscoveryResponse; +import io.grpc.Channel; import io.grpc.Context; import io.grpc.InternalLogId; import io.grpc.ManagedChannel; @@ -36,6 +37,11 @@ import io.grpc.SynchronizationContext.ScheduledHandle; import io.grpc.internal.BackoffPolicy; import io.grpc.stub.StreamObserver; +import io.grpc.xds.Bootstrapper.ServerInfo; +import io.grpc.xds.ClientXdsClient.XdsChannelFactory; +import io.grpc.xds.EnvoyProtoData.Node; +import io.grpc.xds.XdsClient.ResourceStore; +import io.grpc.xds.XdsClient.XdsResponseHandler; import io.grpc.xds.XdsLogger.XdsLogLevel; import java.util.Collection; import java.util.Collections; @@ -48,7 +54,7 @@ * Common base type for XdsClient implementations, which encapsulates the layer abstraction of * the xDS RPC stream. */ -abstract class AbstractXdsClient extends XdsClient { +final class AbstractXdsClient { private static final String ADS_TYPE_URL_LDS_V2 = "type.googleapis.com/envoy.api.v2.Listener"; private static final String ADS_TYPE_URL_LDS = @@ -66,26 +72,18 @@ abstract class AbstractXdsClient extends XdsClient { private static final String ADS_TYPE_URL_EDS = "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment"; - private final SynchronizationContext syncContext = new SynchronizationContext( - new Thread.UncaughtExceptionHandler() { - @Override - public void uncaughtException(Thread t, Throwable e) { - getLogger().log( - XdsLogLevel.ERROR, - "Uncaught exception in XdsClient SynchronizationContext. Panic!", - e); - // TODO(chengyuanzhang): better error handling. - throw new AssertionError(e); - } - }); + private final SynchronizationContext syncContext; private final InternalLogId logId; private final XdsLogger logger; + private final ServerInfo serverInfo; private final ManagedChannel channel; + private final XdsResponseHandler xdsResponseHandler; + private final ResourceStore resourceStore; private final Context context; private final ScheduledExecutorService timeService; private final BackoffPolicy.Provider backoffPolicyProvider; private final Stopwatch stopwatch; - private final Bootstrapper.BootstrapInfo bootstrapInfo; + private final Node bootstrapNode; // Last successfully applied version_info for each resource type. Starts with empty string. // A version_info is used to update management server with client's most recent knowledge of @@ -103,71 +101,42 @@ public void uncaughtException(Thread t, Throwable e) { @Nullable private ScheduledHandle rpcRetryTimer; - AbstractXdsClient(ManagedChannel channel, Bootstrapper.BootstrapInfo bootstrapInfo, - Context context, ScheduledExecutorService timeService, - BackoffPolicy.Provider backoffPolicyProvider, Supplier stopwatchSupplier) { - this.channel = checkNotNull(channel, "channel"); - this.bootstrapInfo = checkNotNull(bootstrapInfo, "bootstrapInfo"); + /** An entity that manages ADS RPCs over a single channel. */ + // TODO: rename to XdsChannel + AbstractXdsClient( + XdsChannelFactory xdsChannelFactory, + ServerInfo serverInfo, + Node bootstrapNode, + XdsResponseHandler xdsResponseHandler, + ResourceStore resourceStore, + Context context, + ScheduledExecutorService + timeService, + SynchronizationContext syncContext, + BackoffPolicy.Provider backoffPolicyProvider, + Supplier stopwatchSupplier) { + this.serverInfo = checkNotNull(serverInfo, "serverInfo"); + this.channel = checkNotNull(xdsChannelFactory, "xdsChannelFactory").create(serverInfo); + this.xdsResponseHandler = checkNotNull(xdsResponseHandler, "xdsResponseHandler"); + this.resourceStore = checkNotNull(resourceStore, "resourcesSubscriber"); + this.bootstrapNode = checkNotNull(bootstrapNode, "bootstrapNode"); this.context = checkNotNull(context, "context"); this.timeService = checkNotNull(timeService, "timeService"); + this.syncContext = checkNotNull(syncContext, "syncContext"); this.backoffPolicyProvider = checkNotNull(backoffPolicyProvider, "backoffPolicyProvider"); stopwatch = checkNotNull(stopwatchSupplier, "stopwatchSupplier").get(); - logId = InternalLogId.allocate("xds-client", null); + logId = InternalLogId.allocate("xds-client", serverInfo.target()); logger = XdsLogger.withLogId(logId); logger.log(XdsLogLevel.INFO, "Created"); } - /** - * Called when an LDS response is received. - */ - // Must be synchronized. - protected void handleLdsResponse(String versionInfo, List resources, String nonce) { - } - - /** - * Called when a RDS response is received. - */ - // Must be synchronized. - protected void handleRdsResponse(String versionInfo, List resources, String nonce) { - } - - /** - * Called when a CDS response is received. - */ - // Must be synchronized. - protected void handleCdsResponse(String versionInfo, List resources, String nonce) { - } - - /** - * Called when an EDS response is received. - */ - // Must be synchronized. - protected void handleEdsResponse(String versionInfo, List resources, String nonce) { - } - - /** - * Called when the ADS stream is closed passively. - */ - // Must be synchronized. - protected void handleStreamClosed(Status error) { - } - - /** - * Called when the ADS stream has been recreated. - */ - // Must be synchronized. - protected void handleStreamRestarted() { - } - - /** - * Called when being shut down. - */ - // Must be synchronized. - protected void handleShutdown() { + /** The underlying channel. */ + // Currently, only externally used for LrsClient. + Channel channel() { + return channel; } - @Override - final void shutdown() { + void shutdown() { syncContext.execute(new Runnable() { @Override public void run() { @@ -179,49 +148,28 @@ public void run() { if (rpcRetryTimer != null && rpcRetryTimer.isPending()) { rpcRetryTimer.cancel(); } - handleShutdown(); + channel.shutdown(); } }); } - @Override - boolean isShutDown() { - return shutdown; - } - - @Override - Bootstrapper.BootstrapInfo getBootstrapInfo() { - return bootstrapInfo; - } - @Override public String toString() { return logId.toString(); } - /** - * Returns the collection of resources currently subscribing to or {@code null} if not - * subscribing to any resources for the given type. - * - *

Note an empty collection indicates subscribing to resources of the given type with - * wildcard mode. - */ - // Must be synchronized. - @Nullable - abstract Collection getSubscribedResources(ResourceType type); - /** * Updates the resource subscription for the given resource type. */ // Must be synchronized. - protected final void adjustResourceSubscription(ResourceType type) { + void adjustResourceSubscription(ResourceType type) { if (isInBackoff()) { return; } if (adsStream == null) { startRpcStream(); } - Collection resources = getSubscribedResources(type); + Collection resources = resourceStore.getSubscribedResources(serverInfo, type); if (resources != null) { adsStream.sendDiscoveryRequest(type, resources); } @@ -232,7 +180,7 @@ protected final void adjustResourceSubscription(ResourceType type) { * and sends an ACK request to the management server. */ // Must be synchronized. - protected final void ackResponse(ResourceType type, String versionInfo, String nonce) { + void ackResponse(ResourceType type, String versionInfo, String nonce) { switch (type) { case LDS: ldsVersion = versionInfo; @@ -252,7 +200,7 @@ protected final void ackResponse(ResourceType type, String versionInfo, String n } logger.log(XdsLogLevel.INFO, "Sending ACK for {0} update, nonce: {1}, current version: {2}", type, nonce, versionInfo); - Collection resources = getSubscribedResources(type); + Collection resources = resourceStore.getSubscribedResources(serverInfo, type); if (resources == null) { resources = Collections.emptyList(); } @@ -264,34 +212,22 @@ protected final void ackResponse(ResourceType type, String versionInfo, String n * accepted version) to the management server. */ // Must be synchronized. - protected final void nackResponse(ResourceType type, String nonce, String errorDetail) { + void nackResponse(ResourceType type, String nonce, String errorDetail) { String versionInfo = getCurrentVersion(type); logger.log(XdsLogLevel.INFO, "Sending NACK for {0} update, nonce: {1}, current version: {2}", type, nonce, versionInfo); - Collection resources = getSubscribedResources(type); + Collection resources = resourceStore.getSubscribedResources(serverInfo, type); if (resources == null) { resources = Collections.emptyList(); } adsStream.sendDiscoveryRequest(type, versionInfo, resources, nonce, errorDetail); } - protected final SynchronizationContext getSyncContext() { - return syncContext; - } - - protected final ScheduledExecutorService getTimeService() { - return timeService; - } - - protected final XdsLogger getLogger() { - return logger; - } - /** * Returns {@code true} if the resource discovery is currently in backoff. */ // Must be synchronized. - protected final boolean isInBackoff() { + boolean isInBackoff() { return rpcRetryTimer != null && rpcRetryTimer.isPending(); } @@ -302,7 +238,7 @@ protected final boolean isInBackoff() { // Must be synchronized. private void startRpcStream() { checkState(adsStream == null, "Previous adsStream has not been cleared yet"); - if (bootstrapInfo.servers().get(0).useProtocolV3()) { + if (serverInfo.useProtocolV3()) { adsStream = new AdsStreamV3(); } else { adsStream = new AdsStreamV2(); @@ -317,8 +253,8 @@ private void startRpcStream() { stopwatch.reset().start(); } + /** Returns the latest accepted version of the given resource type. */ // Must be synchronized. - @Override String getCurrentVersion(ResourceType type) { String version; switch (type) { @@ -353,16 +289,16 @@ public void run() { if (type == ResourceType.UNKNOWN) { continue; } - Collection resources = getSubscribedResources(type); + Collection resources = resourceStore.getSubscribedResources(serverInfo, type); if (resources != null) { adsStream.sendDiscoveryRequest(type, resources); } } - handleStreamRestarted(); + xdsResponseHandler.handleStreamRestarted(serverInfo); } } - protected enum ResourceType { + enum ResourceType { UNKNOWN, LDS, RDS, CDS, EDS; String typeUrl() { @@ -488,19 +424,19 @@ final void handleRpcResponse( switch (type) { case LDS: ldsRespNonce = nonce; - handleLdsResponse(versionInfo, resources, nonce); + xdsResponseHandler.handleLdsResponse(serverInfo, versionInfo, resources, nonce); break; case RDS: rdsRespNonce = nonce; - handleRdsResponse(versionInfo, resources, nonce); + xdsResponseHandler.handleRdsResponse(serverInfo, versionInfo, resources, nonce); break; case CDS: cdsRespNonce = nonce; - handleCdsResponse(versionInfo, resources, nonce); + xdsResponseHandler.handleCdsResponse(serverInfo, versionInfo, resources, nonce); break; case EDS: edsRespNonce = nonce; - handleEdsResponse(versionInfo, resources, nonce); + xdsResponseHandler.handleEdsResponse(serverInfo, versionInfo, resources, nonce); break; case UNKNOWN: default: @@ -526,7 +462,7 @@ private void handleRpcStreamClosed(Status error) { "ADS stream closed with status {0}: {1}. Cause: {2}", error.getCode(), error.getDescription(), error.getCause()); closed = true; - handleStreamClosed(error); + xdsResponseHandler.handleStreamClosed(error); cleanUp(); if (responseReceived || retryBackoffPolicy == null) { // Reset the backoff sequence if had received a response, or backoff sequence @@ -619,7 +555,7 @@ void sendDiscoveryRequest(ResourceType type, String versionInfo, Collection serverChannelMap = new HashMap<>(); private final Map ldsResourceSubscribers = new HashMap<>(); private final Map rdsResourceSubscribers = new HashMap<>(); private final Map cdsResourceSubscribers = new HashMap<>(); private final Map edsResourceSubscribers = new HashMap<>(); private final LoadStatsManager2 loadStatsManager; - private final LoadReportClient lrsClient; + private final Map serverLrsClientMap = new HashMap<>(); + private final XdsChannelFactory xdsChannelFactory; + private final Bootstrapper.BootstrapInfo bootstrapInfo; + private final Context context; + private final ScheduledExecutorService timeService; + private final BackoffPolicy.Provider backoffPolicyProvider; + private final Supplier stopwatchSupplier; private final TimeProvider timeProvider; private boolean reportingLoad; private final TlsContextManager tlsContextManager; + private final InternalLogId logId; + private final XdsLogger logger; + private volatile boolean isShutdown; + // TODO(zdapeng): rename to XdsClientImpl ClientXdsClient( - ManagedChannel channel, Bootstrapper.BootstrapInfo bootstrapInfo, Context context, - ScheduledExecutorService timeService, BackoffPolicy.Provider backoffPolicyProvider, - Supplier stopwatchSupplier, TimeProvider timeProvider, + XdsChannelFactory xdsChannelFactory, + Bootstrapper.BootstrapInfo bootstrapInfo, + Context context, + ScheduledExecutorService timeService, + BackoffPolicy.Provider backoffPolicyProvider, + Supplier stopwatchSupplier, + TimeProvider timeProvider, TlsContextManager tlsContextManager) { - super(channel, bootstrapInfo, context, timeService, backoffPolicyProvider, stopwatchSupplier); + this.xdsChannelFactory = xdsChannelFactory; + this.bootstrapInfo = bootstrapInfo; + this.context = context; + this.timeService = timeService; loadStatsManager = new LoadStatsManager2(stopwatchSupplier); + this.backoffPolicyProvider = backoffPolicyProvider; + this.stopwatchSupplier = stopwatchSupplier; this.timeProvider = timeProvider; this.tlsContextManager = checkNotNull(tlsContextManager, "tlsContextManager"); - lrsClient = new LoadReportClient(loadStatsManager, channel, context, - bootstrapInfo.servers().get(0).useProtocolV3(), bootstrapInfo.node(), - getSyncContext(), timeService, backoffPolicyProvider, stopwatchSupplier); + logId = InternalLogId.allocate("xds-client", null); + logger = XdsLogger.withLogId(logId); + logger.log(XdsLogLevel.INFO, "Created"); + } + + private void maybeCreateXdsChannelWithLrs(ServerInfo serverInfo) { + syncContext.throwIfNotInThisSynchronizationContext(); + if (serverChannelMap.containsKey(serverInfo)) { + return; + } + AbstractXdsClient xdsChannel = new AbstractXdsClient( + xdsChannelFactory, + serverInfo, + bootstrapInfo.node(), + this, + this, + context, + timeService, + syncContext, + backoffPolicyProvider, + stopwatchSupplier); + LoadReportClient lrsClient = new LoadReportClient( + loadStatsManager, xdsChannel.channel(), context, serverInfo.useProtocolV3(), + bootstrapInfo.node(), syncContext, timeService, backoffPolicyProvider, stopwatchSupplier); + serverChannelMap.put(serverInfo, xdsChannel); + serverLrsClientMap.put(serverInfo, lrsClient); } @Override - protected void handleLdsResponse(String versionInfo, List resources, String nonce) { + public void handleLdsResponse( + ServerInfo serverInfo, String versionInfo, List resources, String nonce) { + syncContext.throwIfNotInThisSynchronizationContext(); Map parsedResources = new HashMap<>(resources.size()); Set unpackedResources = new HashSet<>(resources.size()); Set invalidResources = new HashSet<>(); @@ -233,12 +299,12 @@ protected void handleLdsResponse(String versionInfo, List resources, String // LdsUpdate parsed successfully. parsedResources.put(listenerName, new ParsedResource(ldsUpdate, resource)); } - getLogger().log(XdsLogLevel.INFO, + logger.log(XdsLogLevel.INFO, "Received LDS Response version {0} nonce {1}. Parsed resources: {2}", versionInfo, nonce, unpackedResources); handleResourceUpdate( - ResourceType.LDS, parsedResources, invalidResources, retainedRdsResources, versionInfo, - nonce, errors); + serverInfo, ResourceType.LDS, parsedResources, invalidResources, retainedRdsResources, + versionInfo, nonce, errors); } private LdsUpdate processClientSideListener( @@ -1307,7 +1373,9 @@ static StructOrError parseClusterWeight( } @Override - protected void handleRdsResponse(String versionInfo, List resources, String nonce) { + public void handleRdsResponse( + ServerInfo serverInfo, String versionInfo, List resources, String nonce) { + syncContext.throwIfNotInThisSynchronizationContext(); Map parsedResources = new HashMap<>(resources.size()); Set unpackedResources = new HashSet<>(resources.size()); Set invalidResources = new HashSet<>(); @@ -1344,12 +1412,12 @@ protected void handleRdsResponse(String versionInfo, List resources, String parsedResources.put(routeConfigName, new ParsedResource(rdsUpdate, resource)); } - getLogger().log(XdsLogLevel.INFO, + logger.log(XdsLogLevel.INFO, "Received RDS Response version {0} nonce {1}. Parsed resources: {2}", versionInfo, nonce, unpackedResources); handleResourceUpdate( - ResourceType.RDS, parsedResources, invalidResources, Collections.emptySet(), - versionInfo, nonce, errors); + serverInfo, ResourceType.RDS, parsedResources, invalidResources, + Collections.emptySet(), versionInfo, nonce, errors); } private static RdsUpdate processRouteConfiguration( @@ -1370,7 +1438,9 @@ private static RdsUpdate processRouteConfiguration( } @Override - protected void handleCdsResponse(String versionInfo, List resources, String nonce) { + public void handleCdsResponse( + ServerInfo serverInfo, String versionInfo, List resources, String nonce) { + syncContext.throwIfNotInThisSynchronizationContext(); Map parsedResources = new HashMap<>(resources.size()); Set unpackedResources = new HashSet<>(resources.size()); Set invalidResources = new HashSet<>(); @@ -1415,12 +1485,12 @@ protected void handleCdsResponse(String versionInfo, List resources, String } parsedResources.put(clusterName, new ParsedResource(cdsUpdate, resource)); } - getLogger().log(XdsLogLevel.INFO, + logger.log(XdsLogLevel.INFO, "Received CDS Response version {0} nonce {1}. Parsed resources: {2}", versionInfo, nonce, unpackedResources); handleResourceUpdate( - ResourceType.CDS, parsedResources, invalidResources, retainedEdsResources, versionInfo, - nonce, errors); + serverInfo, ResourceType.CDS, parsedResources, invalidResources, retainedEdsResources, + versionInfo, nonce, errors); } @VisibleForTesting @@ -1598,7 +1668,9 @@ private static StructOrError parseNonAggregateCluster( } @Override - protected void handleEdsResponse(String versionInfo, List resources, String nonce) { + public void handleEdsResponse( + ServerInfo serverInfo, String versionInfo, List resources, String nonce) { + syncContext.throwIfNotInThisSynchronizationContext(); Map parsedResources = new HashMap<>(resources.size()); Set unpackedResources = new HashSet<>(resources.size()); Set invalidResources = new HashSet<>(); @@ -1641,12 +1713,12 @@ protected void handleEdsResponse(String versionInfo, List resources, String } parsedResources.put(clusterName, new ParsedResource(edsUpdate, resource)); } - getLogger().log( + logger.log( XdsLogLevel.INFO, "Received EDS Response version {0} nonce {1}. Parsed resources: {2}", versionInfo, nonce, unpackedResources); handleResourceUpdate( - ResourceType.EDS, parsedResources, invalidResources, Collections.emptySet(), - versionInfo, nonce, errors); + serverInfo, ResourceType.EDS, parsedResources, invalidResources, + Collections.emptySet(), versionInfo, nonce, errors); } private static EdsUpdate processClusterLoadAssignment(ClusterLoadAssignment assignment) @@ -1775,7 +1847,8 @@ private static int getRatePerMillion(FractionalPercent percent) { } @Override - protected void handleStreamClosed(Status error) { + public void handleStreamClosed(Status error) { + syncContext.throwIfNotInThisSynchronizationContext(); cleanUpResourceTimers(); for (ResourceSubscriber subscriber : ldsResourceSubscribers.values()) { subscriber.onError(error); @@ -1792,27 +1865,56 @@ protected void handleStreamClosed(Status error) { } @Override - protected void handleStreamRestarted() { + public void handleStreamRestarted(ServerInfo serverInfo) { + syncContext.throwIfNotInThisSynchronizationContext(); for (ResourceSubscriber subscriber : ldsResourceSubscribers.values()) { - subscriber.restartTimer(); + if (subscriber.serverInfo.equals(serverInfo)) { + subscriber.restartTimer(); + } } for (ResourceSubscriber subscriber : rdsResourceSubscribers.values()) { - subscriber.restartTimer(); + if (subscriber.serverInfo.equals(serverInfo)) { + subscriber.restartTimer(); + } } for (ResourceSubscriber subscriber : cdsResourceSubscribers.values()) { - subscriber.restartTimer(); + if (subscriber.serverInfo.equals(serverInfo)) { + subscriber.restartTimer(); + } } for (ResourceSubscriber subscriber : edsResourceSubscribers.values()) { - subscriber.restartTimer(); + if (subscriber.serverInfo.equals(serverInfo)) { + subscriber.restartTimer(); + } } } @Override - protected void handleShutdown() { - if (reportingLoad) { - lrsClient.stopLoadReporting(); - } - cleanUpResourceTimers(); + void shutdown() { + syncContext.execute( + new Runnable() { + @Override + public void run() { + if (isShutdown) { + return; + } + isShutdown = true; + for (AbstractXdsClient xdsChannel : serverChannelMap.values()) { + xdsChannel.shutdown(); + } + if (reportingLoad) { + for (final LoadReportClient lrsClient : serverLrsClientMap.values()) { + lrsClient.stopLoadReporting(); + } + } + cleanUpResourceTimers(); + } + }); + } + + @Override + boolean isShutDown() { + return isShutdown; } private Map getSubscribedResourcesMap(ResourceType type) { @@ -1833,9 +1935,16 @@ private Map getSubscribedResourcesMap(ResourceType t @Nullable @Override - Collection getSubscribedResources(ResourceType type) { + public Collection getSubscribedResources(ServerInfo serverInfo, ResourceType type) { Map resources = getSubscribedResourcesMap(type); - return resources.isEmpty() ? null : resources.keySet(); + ImmutableSet.Builder builder = ImmutableSet.builder(); + for (String key : resources.keySet()) { + if (resources.get(key).serverInfo.equals(serverInfo)) { + builder.add(key); + } + } + Collection retVal = builder.build(); + return retVal.isEmpty() ? null : retVal; } @Override @@ -1854,15 +1963,15 @@ TlsContextManager getTlsContextManager() { @Override void watchLdsResource(final String resourceName, final LdsResourceWatcher watcher) { - getSyncContext().execute(new Runnable() { + syncContext.execute(new Runnable() { @Override public void run() { ResourceSubscriber subscriber = ldsResourceSubscribers.get(resourceName); if (subscriber == null) { - getLogger().log(XdsLogLevel.INFO, "Subscribe LDS resource {0}", resourceName); + logger.log(XdsLogLevel.INFO, "Subscribe LDS resource {0}", resourceName); subscriber = new ResourceSubscriber(ResourceType.LDS, resourceName); ldsResourceSubscribers.put(resourceName, subscriber); - adjustResourceSubscription(ResourceType.LDS); + subscriber.xdsChannel.adjustResourceSubscription(ResourceType.LDS); } subscriber.addWatcher(watcher); } @@ -1871,16 +1980,16 @@ public void run() { @Override void cancelLdsResourceWatch(final String resourceName, final LdsResourceWatcher watcher) { - getSyncContext().execute(new Runnable() { + syncContext.execute(new Runnable() { @Override public void run() { ResourceSubscriber subscriber = ldsResourceSubscribers.get(resourceName); subscriber.removeWatcher(watcher); if (!subscriber.isWatched()) { subscriber.stopTimer(); - getLogger().log(XdsLogLevel.INFO, "Unsubscribe LDS resource {0}", resourceName); + logger.log(XdsLogLevel.INFO, "Unsubscribe LDS resource {0}", resourceName); ldsResourceSubscribers.remove(resourceName); - adjustResourceSubscription(ResourceType.LDS); + subscriber.xdsChannel.adjustResourceSubscription(ResourceType.LDS); } } }); @@ -1888,15 +1997,15 @@ public void run() { @Override void watchRdsResource(final String resourceName, final RdsResourceWatcher watcher) { - getSyncContext().execute(new Runnable() { + syncContext.execute(new Runnable() { @Override public void run() { ResourceSubscriber subscriber = rdsResourceSubscribers.get(resourceName); if (subscriber == null) { - getLogger().log(XdsLogLevel.INFO, "Subscribe RDS resource {0}", resourceName); + logger.log(XdsLogLevel.INFO, "Subscribe RDS resource {0}", resourceName); subscriber = new ResourceSubscriber(ResourceType.RDS, resourceName); rdsResourceSubscribers.put(resourceName, subscriber); - adjustResourceSubscription(ResourceType.RDS); + subscriber.xdsChannel.adjustResourceSubscription(ResourceType.RDS); } subscriber.addWatcher(watcher); } @@ -1905,16 +2014,16 @@ public void run() { @Override void cancelRdsResourceWatch(final String resourceName, final RdsResourceWatcher watcher) { - getSyncContext().execute(new Runnable() { + syncContext.execute(new Runnable() { @Override public void run() { ResourceSubscriber subscriber = rdsResourceSubscribers.get(resourceName); subscriber.removeWatcher(watcher); if (!subscriber.isWatched()) { subscriber.stopTimer(); - getLogger().log(XdsLogLevel.INFO, "Unsubscribe RDS resource {0}", resourceName); + logger.log(XdsLogLevel.INFO, "Unsubscribe RDS resource {0}", resourceName); rdsResourceSubscribers.remove(resourceName); - adjustResourceSubscription(ResourceType.RDS); + subscriber.xdsChannel.adjustResourceSubscription(ResourceType.RDS); } } }); @@ -1922,15 +2031,15 @@ public void run() { @Override void watchCdsResource(final String resourceName, final CdsResourceWatcher watcher) { - getSyncContext().execute(new Runnable() { + syncContext.execute(new Runnable() { @Override public void run() { ResourceSubscriber subscriber = cdsResourceSubscribers.get(resourceName); if (subscriber == null) { - getLogger().log(XdsLogLevel.INFO, "Subscribe CDS resource {0}", resourceName); + logger.log(XdsLogLevel.INFO, "Subscribe CDS resource {0}", resourceName); subscriber = new ResourceSubscriber(ResourceType.CDS, resourceName); cdsResourceSubscribers.put(resourceName, subscriber); - adjustResourceSubscription(ResourceType.CDS); + subscriber.xdsChannel.adjustResourceSubscription(ResourceType.CDS); } subscriber.addWatcher(watcher); } @@ -1939,16 +2048,16 @@ public void run() { @Override void cancelCdsResourceWatch(final String resourceName, final CdsResourceWatcher watcher) { - getSyncContext().execute(new Runnable() { + syncContext.execute(new Runnable() { @Override public void run() { ResourceSubscriber subscriber = cdsResourceSubscribers.get(resourceName); subscriber.removeWatcher(watcher); if (!subscriber.isWatched()) { subscriber.stopTimer(); - getLogger().log(XdsLogLevel.INFO, "Unsubscribe CDS resource {0}", resourceName); + logger.log(XdsLogLevel.INFO, "Unsubscribe CDS resource {0}", resourceName); cdsResourceSubscribers.remove(resourceName); - adjustResourceSubscription(ResourceType.CDS); + subscriber.xdsChannel.adjustResourceSubscription(ResourceType.CDS); } } }); @@ -1956,15 +2065,15 @@ public void run() { @Override void watchEdsResource(final String resourceName, final EdsResourceWatcher watcher) { - getSyncContext().execute(new Runnable() { + syncContext.execute(new Runnable() { @Override public void run() { ResourceSubscriber subscriber = edsResourceSubscribers.get(resourceName); if (subscriber == null) { - getLogger().log(XdsLogLevel.INFO, "Subscribe EDS resource {0}", resourceName); + logger.log(XdsLogLevel.INFO, "Subscribe EDS resource {0}", resourceName); subscriber = new ResourceSubscriber(ResourceType.EDS, resourceName); edsResourceSubscribers.put(resourceName, subscriber); - adjustResourceSubscription(ResourceType.EDS); + subscriber.xdsChannel.adjustResourceSubscription(ResourceType.EDS); } subscriber.addWatcher(watcher); } @@ -1973,30 +2082,32 @@ public void run() { @Override void cancelEdsResourceWatch(final String resourceName, final EdsResourceWatcher watcher) { - getSyncContext().execute(new Runnable() { + syncContext.execute(new Runnable() { @Override public void run() { ResourceSubscriber subscriber = edsResourceSubscribers.get(resourceName); subscriber.removeWatcher(watcher); if (!subscriber.isWatched()) { subscriber.stopTimer(); - getLogger().log(XdsLogLevel.INFO, "Unsubscribe EDS resource {0}", resourceName); + logger.log(XdsLogLevel.INFO, "Unsubscribe EDS resource {0}", resourceName); edsResourceSubscribers.remove(resourceName); - adjustResourceSubscription(ResourceType.EDS); + subscriber.xdsChannel.adjustResourceSubscription(ResourceType.EDS); } } }); } @Override - ClusterDropStats addClusterDropStats(String clusterName, @Nullable String edsServiceName) { + ClusterDropStats addClusterDropStats( + String clusterName, @Nullable String edsServiceName) { ClusterDropStats dropCounter = loadStatsManager.getClusterDropStats(clusterName, edsServiceName); - getSyncContext().execute(new Runnable() { + syncContext.execute(new Runnable() { @Override public void run() { if (!reportingLoad) { - lrsClient.startLoadReporting(); + // TODO(https://github.com/grpc/grpc-java/issues/8628): consume ServerInfo arg. + serverLrsClientMap.values().iterator().next().startLoadReporting(); reportingLoad = true; } } @@ -2005,15 +2116,17 @@ public void run() { } @Override - ClusterLocalityStats addClusterLocalityStats(String clusterName, - @Nullable String edsServiceName, Locality locality) { + ClusterLocalityStats addClusterLocalityStats( + String clusterName, @Nullable String edsServiceName, + Locality locality) { ClusterLocalityStats loadCounter = loadStatsManager.getClusterLocalityStats(clusterName, edsServiceName, locality); - getSyncContext().execute(new Runnable() { + syncContext.execute(new Runnable() { @Override public void run() { if (!reportingLoad) { - lrsClient.startLoadReporting(); + // TODO(https://github.com/grpc/grpc-java/issues/8628): consume ServerInfo arg. + serverLrsClientMap.values().iterator().next().startLoadReporting(); reportingLoad = true; } } @@ -2021,6 +2134,25 @@ public void run() { return loadCounter; } + @Override + Bootstrapper.BootstrapInfo getBootstrapInfo() { + return bootstrapInfo; + } + + // TODO(https://github.com/grpc/grpc-java/issues/8629): remove this + @Override + String getCurrentVersion(ResourceType type) { + if (serverChannelMap.isEmpty()) { + return ""; + } + return serverChannelMap.values().iterator().next().getCurrentVersion(type); + } + + @Override + public String toString() { + return logId.toString(); + } + private void cleanUpResourceTimers() { for (ResourceSubscriber subscriber : ldsResourceSubscribers.values()) { subscriber.stopTimer(); @@ -2037,18 +2169,19 @@ private void cleanUpResourceTimers() { } private void handleResourceUpdate( - ResourceType type, Map parsedResources, Set invalidResources, - Set retainedResources, String version, String nonce, List errors) { + ServerInfo serverInfo, ResourceType type, Map parsedResources, + Set invalidResources, Set retainedResources, String version, String nonce, + List errors) { String errorDetail = null; if (errors.isEmpty()) { checkArgument(invalidResources.isEmpty(), "found invalid resources but missing errors"); - ackResponse(type, version, nonce); + serverChannelMap.get(serverInfo).ackResponse(type, version, nonce); } else { errorDetail = Joiner.on('\n').join(errors); - getLogger().log(XdsLogLevel.WARNING, + logger.log(XdsLogLevel.WARNING, "Failed processing {0} Response version {1} nonce {2}. Errors:\n{3}", type, version, nonce, errorDetail); - nackResponse(type, nonce, errorDetail); + serverChannelMap.get(serverInfo).nackResponse(type, nonce, errorDetail); } long updateTime = timeProvider.currentTimeNanos(); for (Map.Entry entry : getSubscribedResourcesMap(type).entrySet()) { @@ -2123,6 +2256,8 @@ private Any getRawResource() { * Tracks a single subscribed resource. */ private final class ResourceSubscriber { + private final ServerInfo serverInfo; + private final AbstractXdsClient xdsChannel; private final ResourceType type; private final String resource; private final Set watchers = new HashSet<>(); @@ -2132,17 +2267,26 @@ private final class ResourceSubscriber { private ResourceMetadata metadata; ResourceSubscriber(ResourceType type, String resource) { + syncContext.throwIfNotInThisSynchronizationContext(); this.type = type; this.resource = resource; + this.serverInfo = getServerInfo(); // Initialize metadata in UNKNOWN state to cover the case when resource subscriber, // is created but not yet requested because the client is in backoff. this.metadata = ResourceMetadata.newResourceMetadataUnknown(); - if (isInBackoff()) { + maybeCreateXdsChannelWithLrs(serverInfo); + this.xdsChannel = serverChannelMap.get(serverInfo); + if (xdsChannel.isInBackoff()) { return; } restartTimer(); } + // TODO(zdapeng): add resourceName arg and support xdstp:// resources + private ServerInfo getServerInfo() { + return bootstrapInfo.servers().get(0); // use first server + } + void addWatcher(ResourceWatcher watcher) { checkArgument(!watchers.contains(watcher), "watcher %s already registered", watcher); watchers.add(watcher); @@ -2165,7 +2309,7 @@ void restartTimer() { class ResourceNotFound implements Runnable { @Override public void run() { - getLogger().log(XdsLogLevel.INFO, "{0} resource {1} initial fetch timeout", + logger.log(XdsLogLevel.INFO, "{0} resource {1} initial fetch timeout", type, resource); respTimer = null; onAbsent(); @@ -2179,9 +2323,9 @@ public String toString() { // Initial fetch scheduled or rescheduled, transition metadata state to REQUESTED. metadata = ResourceMetadata.newResourceMetadataRequested(); - respTimer = getSyncContext().schedule( + respTimer = syncContext.schedule( new ResourceNotFound(), INITIAL_RESOURCE_FETCH_TIMEOUT_SEC, TimeUnit.SECONDS, - getTimeService()); + timeService); } void stopTimer() { @@ -2216,7 +2360,7 @@ void onAbsent() { if (respTimer != null && respTimer.isPending()) { // too early to conclude absence return; } - getLogger().log(XdsLogLevel.INFO, "Conclude {0} resource {1} not exist", type, resource); + logger.log(XdsLogLevel.INFO, "Conclude {0} resource {1} not exist", type, resource); if (!absent) { data = null; absent = true; @@ -2324,4 +2468,19 @@ String getErrorDetail() { return errorDetail; } } + + abstract static class XdsChannelFactory { + static final XdsChannelFactory DEFAULT_XDS_CHANNEL_FACTORY = new XdsChannelFactory() { + @Override + ManagedChannel create(ServerInfo serverInfo) { + String target = serverInfo.target(); + ChannelCredentials channelCredentials = serverInfo.channelCredentials(); + return Grpc.newChannelBuilder(target, channelCredentials) + .keepAliveTime(5, TimeUnit.MINUTES) + .build(); + } + }; + + abstract ManagedChannel create(ServerInfo serverInfo); + } } diff --git a/xds/src/main/java/io/grpc/xds/LoadReportClient.java b/xds/src/main/java/io/grpc/xds/LoadReportClient.java index 54fa20128bc..af2a673e9f7 100644 --- a/xds/src/main/java/io/grpc/xds/LoadReportClient.java +++ b/xds/src/main/java/io/grpc/xds/LoadReportClient.java @@ -28,9 +28,9 @@ import io.envoyproxy.envoy.service.load_stats.v3.LoadReportingServiceGrpc.LoadReportingServiceStub; import io.envoyproxy.envoy.service.load_stats.v3.LoadStatsRequest; import io.envoyproxy.envoy.service.load_stats.v3.LoadStatsResponse; +import io.grpc.Channel; import io.grpc.Context; import io.grpc.InternalLogId; -import io.grpc.ManagedChannel; import io.grpc.Status; import io.grpc.SynchronizationContext; import io.grpc.SynchronizationContext.ScheduledHandle; @@ -55,7 +55,7 @@ final class LoadReportClient { private final InternalLogId logId; private final XdsLogger logger; - private final ManagedChannel channel; + private final Channel channel; private final Context context; private final boolean useProtocolV3; private final Node node; @@ -75,7 +75,7 @@ final class LoadReportClient { LoadReportClient( LoadStatsManager2 loadStatsManager, - ManagedChannel channel, + Channel channel, Context context, boolean useProtocolV3, Node node, diff --git a/xds/src/main/java/io/grpc/xds/SharedXdsClientPoolProvider.java b/xds/src/main/java/io/grpc/xds/SharedXdsClientPoolProvider.java index 14bdced5dac..1c8fe0bad6d 100644 --- a/xds/src/main/java/io/grpc/xds/SharedXdsClientPoolProvider.java +++ b/xds/src/main/java/io/grpc/xds/SharedXdsClientPoolProvider.java @@ -19,22 +19,18 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.VisibleForTesting; -import io.grpc.ChannelCredentials; import io.grpc.Context; -import io.grpc.Grpc; -import io.grpc.ManagedChannel; import io.grpc.internal.ExponentialBackoffPolicy; import io.grpc.internal.GrpcUtil; import io.grpc.internal.ObjectPool; import io.grpc.internal.SharedResourceHolder; import io.grpc.internal.TimeProvider; import io.grpc.xds.Bootstrapper.BootstrapInfo; -import io.grpc.xds.Bootstrapper.ServerInfo; +import io.grpc.xds.ClientXdsClient.XdsChannelFactory; import io.grpc.xds.XdsNameResolverProvider.XdsClientPoolFactory; import io.grpc.xds.internal.sds.TlsContextManagerImpl; import java.util.Map; import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import javax.annotation.Nullable; import javax.annotation.concurrent.GuardedBy; @@ -113,8 +109,6 @@ static class RefCountedXdsClientObjectPool implements ObjectPool { @GuardedBy("lock") private ScheduledExecutorService scheduler; @GuardedBy("lock") - private ManagedChannel channel; - @GuardedBy("lock") private XdsClient xdsClient; @GuardedBy("lock") private int refCount; @@ -128,16 +122,16 @@ static class RefCountedXdsClientObjectPool implements ObjectPool { public XdsClient getObject() { synchronized (lock) { if (refCount == 0) { - ServerInfo serverInfo = bootstrapInfo.servers().get(0); // use first server - String target = serverInfo.target(); - ChannelCredentials channelCredentials = serverInfo.channelCredentials(); - channel = Grpc.newChannelBuilder(target, channelCredentials) - .keepAliveTime(5, TimeUnit.MINUTES) - .build(); scheduler = SharedResourceHolder.get(GrpcUtil.TIMER_SERVICE); - xdsClient = new ClientXdsClient(channel, bootstrapInfo, context, scheduler, - new ExponentialBackoffPolicy.Provider(), GrpcUtil.STOPWATCH_SUPPLIER, - TimeProvider.SYSTEM_TIME_PROVIDER, new TlsContextManagerImpl(bootstrapInfo)); + xdsClient = new ClientXdsClient( + XdsChannelFactory.DEFAULT_XDS_CHANNEL_FACTORY, + bootstrapInfo, + context, + scheduler, + new ExponentialBackoffPolicy.Provider(), + GrpcUtil.STOPWATCH_SUPPLIER, + TimeProvider.SYSTEM_TIME_PROVIDER, + new TlsContextManagerImpl(bootstrapInfo)); } refCount++; return xdsClient; @@ -151,21 +145,12 @@ public XdsClient returnObject(Object object) { if (refCount == 0) { xdsClient.shutdown(); xdsClient = null; - channel.shutdown(); scheduler = SharedResourceHolder.release(GrpcUtil.TIMER_SERVICE, scheduler); } return null; } } - @VisibleForTesting - @Nullable - ManagedChannel getChannelForTest() { - synchronized (lock) { - return channel; - } - } - @VisibleForTesting @Nullable XdsClient getXdsClientForTest() { diff --git a/xds/src/main/java/io/grpc/xds/XdsClient.java b/xds/src/main/java/io/grpc/xds/XdsClient.java index 6b6be57f042..1daa257e54e 100644 --- a/xds/src/main/java/io/grpc/xds/XdsClient.java +++ b/xds/src/main/java/io/grpc/xds/XdsClient.java @@ -24,6 +24,7 @@ import com.google.protobuf.Any; import io.grpc.Status; import io.grpc.xds.AbstractXdsClient.ResourceType; +import io.grpc.xds.Bootstrapper.ServerInfo; import io.grpc.xds.Endpoints.DropOverload; import io.grpc.xds.Endpoints.LocalityLbEndpoints; import io.grpc.xds.EnvoyServerProtoData.Listener; @@ -31,6 +32,7 @@ import io.grpc.xds.LoadStatsManager2.ClusterDropStats; import io.grpc.xds.LoadStatsManager2.ClusterLocalityStats; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; @@ -495,6 +497,7 @@ TlsContextManager getTlsContextManager() { /** * Returns the latest accepted version of the given resource type. */ + // TODO(https://github.com/grpc/grpc-java/issues/8629): remove this String getCurrentVersion(ResourceType type) { throw new UnsupportedOperationException(); } @@ -566,6 +569,7 @@ void cancelEdsResourceWatch(String resourceName, EdsResourceWatcher watcher) { * use {@link ClusterDropStats#release} to release its hard reference when it is safe to * stop reporting dropped RPCs for the specified cluster in the future. */ + // TODO(https://github.com/grpc/grpc-java/issues/8628): add ServerInfo arg ClusterDropStats addClusterDropStats(String clusterName, @Nullable String edsServiceName) { throw new UnsupportedOperationException(); } @@ -578,8 +582,48 @@ ClusterDropStats addClusterDropStats(String clusterName, @Nullable String edsSer * reference when it is safe to stop reporting RPC loads for the specified locality in the * future. */ + // TODO(https://github.com/grpc/grpc-java/issues/8628): add ServerInfo arg ClusterLocalityStats addClusterLocalityStats( String clusterName, @Nullable String edsServiceName, Locality locality) { throw new UnsupportedOperationException(); } + + interface XdsResponseHandler { + /** Called when an LDS response is received. */ + void handleLdsResponse( + ServerInfo serverInfo, String versionInfo, List resources, String nonce); + + /** Called when an RDS response is received. */ + void handleRdsResponse( + ServerInfo serverInfo, String versionInfo, List resources, String nonce); + + /** Called when an CDS response is received. */ + void handleCdsResponse( + ServerInfo serverInfo, String versionInfo, List resources, String nonce); + + /** Called when an EDS response is received. */ + void handleEdsResponse( + ServerInfo serverInfo, String versionInfo, List resources, String nonce); + + /** Called when the ADS stream is closed passively. */ + // Must be synchronized. + void handleStreamClosed(Status error); + + /** Called when the ADS stream has been recreated. */ + // Must be synchronized. + void handleStreamRestarted(ServerInfo serverInfo); + } + + interface ResourceStore { + /** + * Returns the collection of resources currently subscribing to or {@code null} if not + * subscribing to any resources for the given type. + * + *

Note an empty collection indicates subscribing to resources of the given type with + * wildcard mode. + */ + // Must be synchronized. + @Nullable + Collection getSubscribedResources(ServerInfo serverInfo, ResourceType type); + } } diff --git a/xds/src/test/java/io/grpc/xds/ClientXdsClientTestBase.java b/xds/src/test/java/io/grpc/xds/ClientXdsClientTestBase.java index 5fa9e3da734..9809738c68d 100644 --- a/xds/src/test/java/io/grpc/xds/ClientXdsClientTestBase.java +++ b/xds/src/test/java/io/grpc/xds/ClientXdsClientTestBase.java @@ -59,6 +59,8 @@ import io.grpc.testing.GrpcCleanupRule; import io.grpc.xds.AbstractXdsClient.ResourceType; import io.grpc.xds.Bootstrapper.CertificateProviderInfo; +import io.grpc.xds.Bootstrapper.ServerInfo; +import io.grpc.xds.ClientXdsClient.XdsChannelFactory; import io.grpc.xds.Endpoints.DropOverload; import io.grpc.xds.Endpoints.LbEndpoint; import io.grpc.xds.Endpoints.LocalityLbEndpoints; @@ -272,6 +274,12 @@ public void setUp() throws IOException { .start()); channel = cleanupRule.register(InProcessChannelBuilder.forName(serverName).directExecutor().build()); + XdsChannelFactory xdsChannelFactory = new XdsChannelFactory() { + @Override + ManagedChannel create(ServerInfo serverInfo) { + return channel; + } + }; Bootstrapper.BootstrapInfo bootstrapInfo = Bootstrapper.BootstrapInfo.builder() @@ -284,7 +292,7 @@ public void setUp() throws IOException { .build(); xdsClient = new ClientXdsClient( - channel, + xdsChannelFactory, bootstrapInfo, Context.ROOT, fakeClock.getScheduledExecutorService(), @@ -2325,6 +2333,7 @@ public void streamClosedAndRetryRestartsResourceInitialFetchTimerForUnresolvedRe @Test public void reportLoadStatsToServer() { + xdsClient.watchLdsResource(LDS_RESOURCE, ldsResourceWatcher); String clusterName = "cluster-foo.googleapis.com"; ClusterDropStats dropStats = xdsClient.addClusterDropStats(clusterName, null); LrsRpcCall lrsCall = loadReportCalls.poll(); diff --git a/xds/src/test/java/io/grpc/xds/SharedXdsClientPoolProviderTest.java b/xds/src/test/java/io/grpc/xds/SharedXdsClientPoolProviderTest.java index 6a3cba4ac35..14a8f1ce743 100644 --- a/xds/src/test/java/io/grpc/xds/SharedXdsClientPoolProviderTest.java +++ b/xds/src/test/java/io/grpc/xds/SharedXdsClientPoolProviderTest.java @@ -23,7 +23,6 @@ import static org.mockito.Mockito.when; import io.grpc.InsecureChannelCredentials; -import io.grpc.ManagedChannel; import io.grpc.internal.ObjectPool; import io.grpc.xds.Bootstrapper.BootstrapInfo; import io.grpc.xds.Bootstrapper.ServerInfo; @@ -90,7 +89,6 @@ public void refCountedXdsClientObjectPool_delayedCreation() { BootstrapInfo.builder().servers(Collections.singletonList(server)).node(node).build(); RefCountedXdsClientObjectPool xdsClientPool = new RefCountedXdsClientObjectPool(bootstrapInfo); assertThat(xdsClientPool.getXdsClientForTest()).isNull(); - assertThat(xdsClientPool.getChannelForTest()).isNull(); XdsClient xdsClient = xdsClientPool.getObject(); assertThat(xdsClientPool.getXdsClientForTest()).isNotNull(); xdsClientPool.returnObject(xdsClient); @@ -113,7 +111,6 @@ public void refCountedXdsClientObjectPool_refCounted() { // returnObject twice assertThat(xdsClientPool.returnObject(xdsClient)).isNull(); assertThat(xdsClient.isShutDown()).isTrue(); - assertThat(xdsClientPool.getChannelForTest().isShutdown()).isTrue(); } @Test @@ -123,14 +120,11 @@ public void refCountedXdsClientObjectPool_getObjectCreatesNewInstanceIfAlreadySh BootstrapInfo.builder().servers(Collections.singletonList(server)).node(node).build(); RefCountedXdsClientObjectPool xdsClientPool = new RefCountedXdsClientObjectPool(bootstrapInfo); XdsClient xdsClient1 = xdsClientPool.getObject(); - ManagedChannel channel1 = xdsClientPool.getChannelForTest(); assertThat(xdsClientPool.returnObject(xdsClient1)).isNull(); assertThat(xdsClient1.isShutDown()).isTrue(); - assertThat(channel1.isShutdown()).isTrue(); XdsClient xdsClient2 = xdsClientPool.getObject(); assertThat(xdsClient2).isNotSameInstanceAs(xdsClient1); - assertThat(xdsClientPool.getChannelForTest()).isNotSameInstanceAs(channel1); xdsClientPool.returnObject(xdsClient2); } } From 746501dff6ecf398cfbd0224a882536b9e808b43 Mon Sep 17 00:00:00 2001 From: markb74 <57717302+markb74@users.noreply.github.com> Date: Mon, 1 Nov 2021 18:57:30 +0100 Subject: [PATCH 16/50] binder: SecurityPolicy updates (take 2). (#8637) The previous attempt at this CL relied on guava's Hashing class which is still in beta. This update compares Signature objects directly instead of SHA256 hashs, removing the need for the Hashing class. Add additional comments to the security policy class, to mention that implementing new policies requires significant care. With that in mind, add security policies to check the peer app's signature, so people can create cross-app communication without having to implement their own policy. Finally, add the UntrustedSecurityPolicies class, since that's inevitably a policy which is sometimes needed. --- .../java/io/grpc/binder/SecurityPolicies.java | 132 ++++++++++++++++++ .../java/io/grpc/binder/SecurityPolicy.java | 5 + .../binder/UntrustedSecurityPolicies.java | 47 +++++++ .../io/grpc/binder/SecurityPoliciesTest.java | 118 ++++++++++++++++ 4 files changed, 302 insertions(+) create mode 100644 binder/src/main/java/io/grpc/binder/UntrustedSecurityPolicies.java diff --git a/binder/src/main/java/io/grpc/binder/SecurityPolicies.java b/binder/src/main/java/io/grpc/binder/SecurityPolicies.java index be46b9e3e54..dcf36be00ca 100644 --- a/binder/src/main/java/io/grpc/binder/SecurityPolicies.java +++ b/binder/src/main/java/io/grpc/binder/SecurityPolicies.java @@ -16,9 +16,20 @@ package io.grpc.binder; +import android.annotation.SuppressLint; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.Signature; +import android.os.Build; import android.os.Process; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; import io.grpc.ExperimentalApi; import io.grpc.Status; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; import javax.annotation.CheckReturnValue; /** Static factory methods for creating standard security policies. */ @@ -55,4 +66,125 @@ public Status checkAuthorization(int uid) { } }; } + + /** + * Creates a {@link SecurityPolicy} which checks if the package signature + * matches {@code requiredSignature}. + * + * @param packageName the package name of the allowed package. + * @param requiredSignature the allowed signature of the allowed package. + * @throws NullPointerException if any of the inputs are {@code null}. + */ + public static SecurityPolicy hasSignature( + PackageManager packageManager, String packageName, Signature requiredSignature) { + return oneOfSignatures( + packageManager, packageName, ImmutableList.of(requiredSignature)); + } + + /** + * Creates a {@link SecurityPolicy} which checks if the package signature + * matches any of {@code requiredSignatures}. + * + * @param packageName the package name of the allowed package. + * @param requiredSignatures the allowed signatures of the allowed package. + * @throws NullPointerException if any of the inputs are {@code null}. + * @throws IllegalArgumentException if {@code requiredSignatures} is empty. + */ + public static SecurityPolicy oneOfSignatures( + PackageManager packageManager, + String packageName, + Collection requiredSignatures) { + Preconditions.checkNotNull(packageManager, "packageManager"); + Preconditions.checkNotNull(packageName, "packageName"); + Preconditions.checkNotNull(requiredSignatures, "requiredSignatures"); + Preconditions.checkArgument(!requiredSignatures.isEmpty(), + "requiredSignatures"); + ImmutableList requiredSignaturesImmutable = ImmutableList.copyOf(requiredSignatures); + + for (Signature requiredSignature : requiredSignaturesImmutable) { + Preconditions.checkNotNull(requiredSignature); + } + + return new SecurityPolicy() { + @Override + public Status checkAuthorization(int uid) { + return checkUidSignature( + packageManager, uid, packageName, requiredSignaturesImmutable); + } + }; + } + + private static Status checkUidSignature( + PackageManager packageManager, + int uid, + String packageName, + ImmutableList requiredSignatures) { + String[] packages = packageManager.getPackagesForUid(uid); + if (packages == null) { + return Status.UNAUTHENTICATED.withDescription( + "Rejected by signature check security policy"); + } + boolean packageNameMatched = false; + for (String pkg : packages) { + if (!packageName.equals(pkg)) { + continue; + } + packageNameMatched = true; + if (checkPackageSignature(packageManager, pkg, requiredSignatures)) { + return Status.OK; + } + } + return Status.PERMISSION_DENIED.withDescription( + "Rejected by signature check security policy. Package name matched: " + + packageNameMatched); + } + + /** + * Checks if the signature of {@code packageName} matches one of the given signatures. + * + * @param packageName the package to be checked + * @param requiredSignatures list of signatures. + * @return {@code true} if {@code packageName} has a matching signature. + */ + @SuppressWarnings("deprecation") // For PackageInfo.signatures + @SuppressLint("PackageManagerGetSignatures") // We only allow 1 signature. + private static boolean checkPackageSignature( + PackageManager packageManager, + String packageName, + ImmutableList requiredSignatures) { + PackageInfo packageInfo; + try { + if (Build.VERSION.SDK_INT >= 28) { + packageInfo = + packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNING_CERTIFICATES); + if (packageInfo.signingInfo == null) { + return false; + } + Signature[] signatures = + packageInfo.signingInfo.hasMultipleSigners() + ? packageInfo.signingInfo.getApkContentsSigners() + : packageInfo.signingInfo.getSigningCertificateHistory(); + + for (Signature signature : signatures) { + if (requiredSignatures.contains(signature)) { + return true; + } + } + } else { + packageInfo = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES); + if (packageInfo.signatures == null || packageInfo.signatures.length != 1) { + // Reject multiply-signed apks because of b/13678484 + // (See PackageManagerGetSignatures supression above). + return false; + } + + if (requiredSignatures.contains(packageInfo.signatures[0])) { + return true; + } + } + } catch (NameNotFoundException nnfe) { + return false; + } + return false; + } } diff --git a/binder/src/main/java/io/grpc/binder/SecurityPolicy.java b/binder/src/main/java/io/grpc/binder/SecurityPolicy.java index d7dad53fdc8..d13f3a863fd 100644 --- a/binder/src/main/java/io/grpc/binder/SecurityPolicy.java +++ b/binder/src/main/java/io/grpc/binder/SecurityPolicy.java @@ -23,6 +23,11 @@ /** * Decides whether a given Android UID is authorized to access some resource. * + * While it's possible to extend this class to define your own policy, it's strongly + * recommended that you only use the policies provided by the {@link SecurityPolicies} or + * {@link UntrustedSecurityPolicies} classes. Implementing your own security policy requires + * significant care, and an understanding of the details and pitfalls of Android security. + * *

IMPORTANT For any concrete extensions of this class, it's assumed that the * authorization status of a given UID will not change as long as a process with that UID is * alive. diff --git a/binder/src/main/java/io/grpc/binder/UntrustedSecurityPolicies.java b/binder/src/main/java/io/grpc/binder/UntrustedSecurityPolicies.java new file mode 100644 index 00000000000..7c842b025ac --- /dev/null +++ b/binder/src/main/java/io/grpc/binder/UntrustedSecurityPolicies.java @@ -0,0 +1,47 @@ +/* + * Copyright 2021 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.binder; + +import io.grpc.ExperimentalApi; +import io.grpc.Status; +import javax.annotation.CheckReturnValue; + +/** + * Static factory methods for creating untrusted security policies. + */ +@CheckReturnValue +@ExperimentalApi("https://github.com/grpc/grpc-java/issues/8022") +public final class UntrustedSecurityPolicies { + + private UntrustedSecurityPolicies() {} + + /** + * Return a security policy which allows any peer on device. + * Servers should only use this policy if they intend to expose + * a service to all applications on device. + * Clients should only use this policy if they don't need to trust the + * application they're connecting to. + */ + public static SecurityPolicy untrustedPublic() { + return new SecurityPolicy() { + @Override + public Status checkAuthorization(int uid) { + return Status.OK; + } + }; + } +} diff --git a/binder/src/test/java/io/grpc/binder/SecurityPoliciesTest.java b/binder/src/test/java/io/grpc/binder/SecurityPoliciesTest.java index 6fd9e22ebaa..86edb5ad7df 100644 --- a/binder/src/test/java/io/grpc/binder/SecurityPoliciesTest.java +++ b/binder/src/test/java/io/grpc/binder/SecurityPoliciesTest.java @@ -17,22 +17,64 @@ package io.grpc.binder; import static com.google.common.truth.Truth.assertThat; +import static org.robolectric.Shadows.shadowOf; +import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.Signature; import android.os.Process; +import androidx.test.core.app.ApplicationProvider; +import com.google.common.collect.ImmutableList; import io.grpc.Status; +import io.grpc.binder.SecurityPolicy; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; @RunWith(RobolectricTestRunner.class) public final class SecurityPoliciesTest { + private static final int MY_UID = Process.myUid(); private static final int OTHER_UID = MY_UID + 1; + private static final int OTHER_UID_SAME_SIGNATURE = MY_UID + 2; + private static final int OTHER_UID_NO_SIGNATURE = MY_UID + 3; + private static final int OTHER_UID_UNKNOWN = MY_UID + 4; private static final String PERMISSION_DENIED_REASONS = "some reasons"; + private static final Signature SIG1 = new Signature("1234"); + private static final Signature SIG2 = new Signature("4321"); + + private static final String OTHER_UID_PACKAGE_NAME = "other.package"; + private static final String OTHER_UID_SAME_SIGNATURE_PACKAGE_NAME = "other.package.samesignature"; + private static final String OTHER_UID_NO_SIGNATURE_PACKAGE_NAME = "other.package.nosignature"; + + private Context appContext; + private PackageManager packageManager; + private SecurityPolicy policy; + @Before + public void setUp() { + appContext = ApplicationProvider.getApplicationContext(); + packageManager = appContext.getPackageManager(); + installPackage(MY_UID, appContext.getPackageName(), SIG1); + installPackage(OTHER_UID, OTHER_UID_PACKAGE_NAME, SIG2); + installPackage(OTHER_UID_SAME_SIGNATURE, OTHER_UID_SAME_SIGNATURE_PACKAGE_NAME, SIG1); + installPackage(OTHER_UID_NO_SIGNATURE, OTHER_UID_NO_SIGNATURE_PACKAGE_NAME); + } + + @SuppressWarnings("deprecation") + private void installPackage(int uid, String packageName, Signature... signatures) { + PackageInfo info = new PackageInfo(); + info.packageName = packageName; + info.signatures = signatures; + shadowOf(packageManager).installPackage(info); + shadowOf(packageManager).setPackagesForUid(uid, packageName); + } + @Test public void testInternalOnly() throws Exception { policy = SecurityPolicies.internalOnly(); @@ -53,4 +95,80 @@ public void testPermissionDenied() throws Exception { assertThat(policy.checkAuthorization(OTHER_UID).getDescription()) .isEqualTo(PERMISSION_DENIED_REASONS); } + + @Test + public void testHasSignature_succeedsIfPackageNameAndSignaturesMatch() + throws Exception { + policy = SecurityPolicies.hasSignature(packageManager, OTHER_UID_PACKAGE_NAME, SIG2); + + // THEN UID for package that has SIG2 will be authorized + assertThat(policy.checkAuthorization(OTHER_UID).getCode()).isEqualTo(Status.OK.getCode()); + } + + @Test + public void testHasSignature_failsIfPackageNameDoesNotMatch() throws Exception { + policy = SecurityPolicies.hasSignature(packageManager, appContext.getPackageName(), SIG1); + + // THEN UID for package that has SIG1 but different package name will not be authorized + assertThat(policy.checkAuthorization(OTHER_UID_SAME_SIGNATURE).getCode()) + .isEqualTo(Status.PERMISSION_DENIED.getCode()); + } + + @Test + public void testHasSignature_failsIfSignatureDoesNotMatch() throws Exception { + policy = SecurityPolicies.hasSignature(packageManager, OTHER_UID_PACKAGE_NAME, SIG1); + + // THEN UID for package that doesn't have SIG1 will not be authorized + assertThat(policy.checkAuthorization(OTHER_UID).getCode()) + .isEqualTo(Status.PERMISSION_DENIED.getCode()); + } + + @Test + public void testOneOfSignatures_succeedsIfPackageNameAndSignaturesMatch() + throws Exception { + policy = + SecurityPolicies.oneOfSignatures( + packageManager, OTHER_UID_PACKAGE_NAME, ImmutableList.of(SIG2)); + + // THEN UID for package that has SIG2 will be authorized + assertThat(policy.checkAuthorization(OTHER_UID).getCode()).isEqualTo(Status.OK.getCode()); + } + + @Test + public void testOneOfSignature_failsIfAllSignaturesDoNotMatch() throws Exception { + policy = + SecurityPolicies.oneOfSignatures( + packageManager, + appContext.getPackageName(), + ImmutableList.of(SIG1, new Signature("1314"))); + + // THEN UID for package that has SIG1 but different package name will not be authorized + assertThat(policy.checkAuthorization(OTHER_UID_SAME_SIGNATURE).getCode()) + .isEqualTo(Status.PERMISSION_DENIED.getCode()); + } + + @Test + public void testOneOfSignature_succeedsIfPackageNameAndOneOfSignaturesMatch() + throws Exception { + policy = + SecurityPolicies.oneOfSignatures( + packageManager, + OTHER_UID_PACKAGE_NAME, + ImmutableList.of(SIG1, SIG2)); + + // THEN UID for package that has SIG2 will be authorized + assertThat(policy.checkAuthorization(OTHER_UID).getCode()).isEqualTo(Status.OK.getCode()); + } + + @Test + public void testHasSignature_failsIfUidUnknown() throws Exception { + policy = + SecurityPolicies.hasSignature( + packageManager, + appContext.getPackageName(), + SIG1); + + assertThat(policy.checkAuthorization(OTHER_UID_UNKNOWN).getCode()) + .isEqualTo(Status.UNAUTHENTICATED.getCode()); + } } From c1e19af86dea0b9a8725969a95e116029397ad4d Mon Sep 17 00:00:00 2001 From: Terry Wilson Date: Tue, 2 Nov 2021 12:47:47 -0700 Subject: [PATCH 17/50] grpclb: fallback timer only when not already using fallback backends. (#8646) Addresses a problem where we initially only resolve addresses to the backends, but not the load balancer and then later resolve addresses to both. In this situation the fallback timer was started during the second instance even if it resulted in the timer later failing as we were already using fallback backends. This change assures that a fallback time is only ever started if we are not already using the fallback backends. This is a follow-up fix to #8253. --- .../main/java/io/grpc/grpclb/GrpclbState.java | 5 ++-- .../grpc/grpclb/GrpclbLoadBalancerTest.java | 27 +++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/grpclb/src/main/java/io/grpc/grpclb/GrpclbState.java b/grpclb/src/main/java/io/grpc/grpclb/GrpclbState.java index 8607d3996a5..1eebaa63a8e 100644 --- a/grpclb/src/main/java/io/grpc/grpclb/GrpclbState.java +++ b/grpclb/src/main/java/io/grpc/grpclb/GrpclbState.java @@ -287,8 +287,9 @@ void handleAddresses( cancelLbRpcRetryTimer(); startLbRpc(); } - // Start the fallback timer if it's never started - if (fallbackTimer == null) { + // Start the fallback timer if it's never started and we are not already using fallback + // backends. + if (fallbackTimer == null && !usingFallbackBackends) { fallbackTimer = syncContext.schedule( new FallbackModeTask(BALANCER_TIMEOUT_STATUS), FALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS, timerService); diff --git a/grpclb/src/test/java/io/grpc/grpclb/GrpclbLoadBalancerTest.java b/grpclb/src/test/java/io/grpc/grpclb/GrpclbLoadBalancerTest.java index cb231c6c055..293c0aa0b82 100644 --- a/grpclb/src/test/java/io/grpc/grpclb/GrpclbLoadBalancerTest.java +++ b/grpclb/src/test/java/io/grpc/grpclb/GrpclbLoadBalancerTest.java @@ -1462,6 +1462,33 @@ public void grpclbFallback_noBalancerAddress() { .updateBalancingState(eq(TRANSIENT_FAILURE), any(SubchannelPicker.class)); } + /** + * A test for a situation where we first only get backend addresses resolved and then in a + * later name resolution get both backend and load balancer addresses. The first instance + * will switch us to using fallback backends and it is important that in the second instance + * we do not start a fallback timer as it will fail when it triggers if the fallback backends + * are already in use. + */ + @Test + public void grpclbFallback_noTimerWhenAlreadyInFallback() { + // Initially we only get backend addresses without any LB ones. This should get us to use + // fallback backends from the start as we won't be able to even talk to the load balancer. + // No fallback timer would be started as we already started to use fallback backends. + deliverResolvedAddresses(createResolvedBalancerAddresses(1), + Collections.emptyList()); + assertEquals(0, fakeClock.numPendingTasks(FALLBACK_MODE_TASK_FILTER)); + + // Later a new name resolution call happens and we get both backend and LB addresses. Since we + // are already operating with fallback backends a fallback timer should not be started to move + // us to fallback mode. + deliverResolvedAddresses(Collections.emptyList(), + createResolvedBalancerAddresses(1)); + + // If a fallback timer is started it will eventually throw an exception when it tries to switch + // us to using fallback backends when we already are using them. + assertEquals(0, fakeClock.numPendingTasks(FALLBACK_MODE_TASK_FILTER)); + } + @Test public void grpclbFallback_balancerLost() { subtestGrpclbFallbackConnectionLost(true, false); From 0000cba665c69958355b639474c7387d98afcc79 Mon Sep 17 00:00:00 2001 From: yifeizhuang Date: Tue, 2 Nov 2021 13:20:41 -0700 Subject: [PATCH 18/50] xds: add xds end to end interop test (#8618) Add AbstractXdsInteropTest, XdsTestControlPlaneService and only ping-pong testcase in initial implementation. AbstractXdsInteropTest sets up the test control plane, create xdsClient and xdServer using bootstrap override, test case extending AbstractXdsInteropTest is supposed to override the control plane config and run the verification. XdsTestControlPlaneService only has static xds configurations, not able to keep states. How to run: ./gradlew :grpc-interop-testing:installDist -PskipCodegen=true ./interop-testing/build/install/grpc-interop-testing/bin/xds-e2e-test-client --- interop-testing/build.gradle | 8 + .../integration/AbstractXdsInteropTest.java | 337 ++++++++++++++++++ .../testing/integration/XdsInteropTest.java | 57 +++ .../XdsTestControlPlaneService.java | 269 ++++++++++++++ 4 files changed, 671 insertions(+) create mode 100644 interop-testing/src/main/java/io/grpc/testing/integration/AbstractXdsInteropTest.java create mode 100644 interop-testing/src/main/java/io/grpc/testing/integration/XdsInteropTest.java create mode 100644 interop-testing/src/main/java/io/grpc/testing/integration/XdsTestControlPlaneService.java diff --git a/interop-testing/build.gradle b/interop-testing/build.gradle index 944c0daab81..ef7510c1723 100644 --- a/interop-testing/build.gradle +++ b/interop-testing/build.gradle @@ -147,6 +147,13 @@ task xds_test_server(type: CreateStartScripts) { classpath = startScripts.classpath } +task xds_e2e_client(type: CreateStartScripts) { + mainClassName = "io.grpc.testing.integration.XdsInteropTest" + applicationName = "xds-e2e-test-client" + outputDir = new File(project.buildDir, 'tmp/scripts/' + name) + classpath = startScripts.classpath +} + applicationDistribution.into("bin") { from(test_client) from(test_server) @@ -157,6 +164,7 @@ applicationDistribution.into("bin") { from(grpclb_fallback_test_client) from(xds_test_client) from(xds_test_server) + from(xds_e2e_client) fileMode = 0755 } diff --git a/interop-testing/src/main/java/io/grpc/testing/integration/AbstractXdsInteropTest.java b/interop-testing/src/main/java/io/grpc/testing/integration/AbstractXdsInteropTest.java new file mode 100644 index 00000000000..0bd2318b0fc --- /dev/null +++ b/interop-testing/src/main/java/io/grpc/testing/integration/AbstractXdsInteropTest.java @@ -0,0 +1,337 @@ +/* + * Copyright 2021 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.testing.integration; + +import com.google.common.collect.ImmutableMap; +import com.google.common.util.concurrent.MoreExecutors; +import com.google.protobuf.Any; +import com.google.protobuf.UInt32Value; +import io.grpc.Grpc; +import io.grpc.InsecureChannelCredentials; +import io.grpc.InsecureServerCredentials; +import io.grpc.ManagedChannel; +import io.grpc.NameResolverRegistry; +import io.grpc.Server; +import io.grpc.netty.NettyServerBuilder; +import io.grpc.xds.XdsNameResolverProvider; +import io.grpc.xds.XdsServerBuilder; +import io.grpc.xds.shaded.io.envoyproxy.envoy.config.cluster.v3.Cluster; +import io.grpc.xds.shaded.io.envoyproxy.envoy.config.core.v3.Address; +import io.grpc.xds.shaded.io.envoyproxy.envoy.config.core.v3.AggregatedConfigSource; +import io.grpc.xds.shaded.io.envoyproxy.envoy.config.core.v3.ConfigSource; +import io.grpc.xds.shaded.io.envoyproxy.envoy.config.core.v3.HealthStatus; +import io.grpc.xds.shaded.io.envoyproxy.envoy.config.core.v3.SocketAddress; +import io.grpc.xds.shaded.io.envoyproxy.envoy.config.core.v3.TrafficDirection; +import io.grpc.xds.shaded.io.envoyproxy.envoy.config.endpoint.v3.ClusterLoadAssignment; +import io.grpc.xds.shaded.io.envoyproxy.envoy.config.endpoint.v3.Endpoint; +import io.grpc.xds.shaded.io.envoyproxy.envoy.config.endpoint.v3.LbEndpoint; +import io.grpc.xds.shaded.io.envoyproxy.envoy.config.endpoint.v3.LocalityLbEndpoints; +import io.grpc.xds.shaded.io.envoyproxy.envoy.config.listener.v3.ApiListener; +import io.grpc.xds.shaded.io.envoyproxy.envoy.config.listener.v3.Filter; +import io.grpc.xds.shaded.io.envoyproxy.envoy.config.listener.v3.FilterChain; +import io.grpc.xds.shaded.io.envoyproxy.envoy.config.listener.v3.FilterChainMatch; +import io.grpc.xds.shaded.io.envoyproxy.envoy.config.listener.v3.Listener; +import io.grpc.xds.shaded.io.envoyproxy.envoy.config.route.v3.NonForwardingAction; +import io.grpc.xds.shaded.io.envoyproxy.envoy.config.route.v3.Route; +import io.grpc.xds.shaded.io.envoyproxy.envoy.config.route.v3.RouteAction; +import io.grpc.xds.shaded.io.envoyproxy.envoy.config.route.v3.RouteConfiguration; +import io.grpc.xds.shaded.io.envoyproxy.envoy.config.route.v3.RouteMatch; +import io.grpc.xds.shaded.io.envoyproxy.envoy.config.route.v3.VirtualHost; +import io.grpc.xds.shaded.io.envoyproxy.envoy.extensions.filters.http.router.v3.Router; +import io.grpc.xds.shaded.io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager; +import io.grpc.xds.shaded.io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.HttpFilter; +import io.grpc.xds.shaded.io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.Rds; + +import java.util.Collections; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Abstract base class for end-to-end xds tests. + * A local control plane is implemented in {@link XdsTestControlPlaneService}. + * Test cases can inject xds configs to the control plane for testing. + */ +public abstract class AbstractXdsInteropTest { + private static final Logger logger = Logger.getLogger(AbstractXdsInteropTest.class.getName()); + + protected static final int testServerPort = 8080; + private static final int controlPlaneServicePort = 443; + private Server server; + private Server controlPlane; + protected TestServiceGrpc.TestServiceBlockingStub blockingStub; + private ScheduledExecutorService executor; + private XdsNameResolverProvider nameResolverProvider; + private static final String scheme = "test-xds"; + private static final String serverHostName = "0.0.0.0:" + testServerPort; + private static final String SERVER_LISTENER_TEMPLATE = + "grpc/server?udpa.resource.listening_address=%s"; + private static final String rdsName = "route-config.googleapis.com"; + private static final String clusterName = "cluster0"; + private static final String edsName = "eds-service-0"; + private static final String HTTP_CONNECTION_MANAGER_TYPE_URL = + "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3" + + ".HttpConnectionManager"; + + private static final Map defaultClientBootstrapOverride = ImmutableMap.of( + "node", ImmutableMap.of( + "id", UUID.randomUUID().toString(), + "cluster", "cluster0"), + "xds_servers", Collections.singletonList( + ImmutableMap.of( + "server_uri", "localhost:" + controlPlaneServicePort, + "channel_creds", Collections.singletonList( + ImmutableMap.of("type", "insecure") + ), + "server_features", Collections.singletonList("xds_v3") + ) + ) + ); + + /** + * Provides default client bootstrap. + * A subclass test case should override this method if it tests client bootstrap. + */ + protected Map getClientBootstrapOverride() { + return defaultClientBootstrapOverride; + } + + private static final Map defaultServerBootstrapOverride = ImmutableMap.of( + "node", ImmutableMap.of( + "id", UUID.randomUUID().toString()), + "xds_servers", Collections.singletonList( + ImmutableMap.of( + "server_uri", "localhost:" + controlPlaneServicePort, + "channel_creds", Collections.singletonList( + ImmutableMap.of("type", "insecure") + ), + "server_features", Collections.singletonList("xds_v3") + ) + ), + "server_listener_resource_name_template", SERVER_LISTENER_TEMPLATE + ); + + /** + * Provides default server bootstrap. + * A subclass test case should override this method if it tests server bootstrap. + */ + protected Map getServerBootstrapOverride() { + return defaultServerBootstrapOverride; + } + + protected void setUp() throws Exception { + startControlPlane(); + startServer(); + nameResolverProvider = XdsNameResolverProvider.createForTest(scheme, + getClientBootstrapOverride()); + NameResolverRegistry.getDefaultRegistry().register(nameResolverProvider); + ManagedChannel channel = Grpc.newChannelBuilder(scheme + ":///" + serverHostName, + InsecureChannelCredentials.create()).build(); + blockingStub = TestServiceGrpc.newBlockingStub(channel); + } + + protected void tearDown() throws Exception { + if (server != null) { + server.shutdownNow(); + if (!server.awaitTermination(5, TimeUnit.SECONDS)) { + logger.log(Level.SEVERE, "Timed out waiting for server shutdown"); + } + } + if (controlPlane != null) { + controlPlane.shutdownNow(); + if (!controlPlane.awaitTermination(5, TimeUnit.SECONDS)) { + logger.log(Level.SEVERE, "Timed out waiting for server shutdown"); + } + } + if (executor != null) { + MoreExecutors.shutdownAndAwaitTermination(executor, 5, TimeUnit.SECONDS); + } + NameResolverRegistry.getDefaultRegistry().deregister(nameResolverProvider); + } + + protected void startServer() throws Exception { + executor = Executors.newSingleThreadScheduledExecutor(); + XdsServerBuilder serverBuilder = XdsServerBuilder.forPort( + testServerPort, InsecureServerCredentials.create()) + .addService(new TestServiceImpl(executor)) + .overrideBootstrapForTest(getServerBootstrapOverride()); + server = serverBuilder.build().start(); + } + + /** + * Provides default control plane xds configs. + * A subclass test case should override this method to inject control plane xds configs to verify + * end-to-end behavior. + */ + protected XdsTestControlPlaneService.XdsTestControlPlaneConfig getControlPlaneConfig() { + String tcpListenerName = SERVER_LISTENER_TEMPLATE.replaceAll("%s", serverHostName); + return new XdsTestControlPlaneService.XdsTestControlPlaneConfig( + Collections.singletonList(serverListener(tcpListenerName, serverHostName)), + Collections.singletonList(clientListener(serverHostName)), + Collections.singletonList(rds(serverHostName)), + Collections.singletonList(cds()), + Collections.singletonList(eds(testServerPort)) + ); + } + + private void startControlPlane() throws Exception { + XdsTestControlPlaneService.XdsTestControlPlaneConfig controlPlaneConfig = + getControlPlaneConfig(); + logger.log(Level.FINER, "Starting control plane with config: {0}", controlPlaneConfig); + XdsTestControlPlaneService controlPlaneService = new XdsTestControlPlaneService( + controlPlaneConfig); + NettyServerBuilder controlPlaneServerBuilder = + NettyServerBuilder.forPort(controlPlaneServicePort) + .addService(controlPlaneService); + controlPlane = controlPlaneServerBuilder.build().start(); + } + + /** + * A subclass test case should override this method to verify end-to-end behaviour. + */ + abstract void run(); + + private static Listener clientListener(String name) { + HttpFilter httpFilter = HttpFilter.newBuilder() + .setName("terminal-filter") + .setTypedConfig(Any.pack(Router.newBuilder().build())) + .setIsOptional(true) + .build(); + ApiListener apiListener = ApiListener.newBuilder().setApiListener(Any.pack( + HttpConnectionManager.newBuilder() + .setRds( + Rds.newBuilder() + .setRouteConfigName(rdsName) + .setConfigSource( + ConfigSource.newBuilder() + .setAds(AggregatedConfigSource.getDefaultInstance()))) + .addAllHttpFilters(Collections.singletonList(httpFilter)) + .build(), + HTTP_CONNECTION_MANAGER_TYPE_URL) + ).build(); + Listener listener = Listener.newBuilder() + .setName(name) + .setApiListener(apiListener).build(); + return listener; + } + + private static Listener serverListener(String name, String authority) { + HttpFilter routerFilter = HttpFilter.newBuilder() + .setName("terminal-filter") + .setTypedConfig( + Any.pack(Router.newBuilder().build())) + .setIsOptional(true) + .build(); + VirtualHost virtualHost = VirtualHost.newBuilder() + .setName("virtual-host-0") + .addDomains(authority) + .addRoutes( + Route.newBuilder() + .setMatch( + RouteMatch.newBuilder().setPrefix("/").build() + ) + .setNonForwardingAction(NonForwardingAction.newBuilder().build()) + .build() + ).build(); + RouteConfiguration routeConfig = RouteConfiguration.newBuilder() + .addVirtualHosts(virtualHost) + .build(); + Filter filter = Filter.newBuilder() + .setName("network-filter-0") + .setTypedConfig( + Any.pack( + HttpConnectionManager.newBuilder() + .setRouteConfig(routeConfig) + .addAllHttpFilters(Collections.singletonList(routerFilter)) + .build() + ) + ).build(); + FilterChainMatch filterChainMatch = FilterChainMatch.newBuilder() + .setSourceType(FilterChainMatch.ConnectionSourceType.ANY) + .build(); + FilterChain filterChain = FilterChain.newBuilder() + .setName("filter-chain-0") + .setFilterChainMatch(filterChainMatch) + .addFilters(filter) + .build(); + return Listener.newBuilder() + .setName(name) + .setTrafficDirection(TrafficDirection.INBOUND) + .addFilterChains(filterChain) + .build(); + } + + private static RouteConfiguration rds(String authority) { + VirtualHost virtualHost = VirtualHost.newBuilder() + .addDomains(authority) + .addRoutes( + Route.newBuilder() + .setMatch( + RouteMatch.newBuilder().setPrefix("/").build() + ) + .setRoute( + RouteAction.newBuilder().setCluster(clusterName).build() + ) + .build()) + .build(); + return RouteConfiguration.newBuilder().setName(rdsName).addVirtualHosts(virtualHost).build(); + } + + private static Cluster cds() { + return Cluster.newBuilder() + .setName(clusterName) + .setType(Cluster.DiscoveryType.EDS) + .setEdsClusterConfig( + Cluster.EdsClusterConfig.newBuilder() + .setServiceName(edsName) + .setEdsConfig( + ConfigSource.newBuilder() + .setAds(AggregatedConfigSource.newBuilder().build()) + .build()) + .build() + ) + .setLbPolicy(Cluster.LbPolicy.ROUND_ROBIN) + .build(); + } + + private static ClusterLoadAssignment eds(int port) { + Address address = Address.newBuilder() + .setSocketAddress( + SocketAddress.newBuilder().setAddress("0.0.0.0").setPortValue(port).build() + ) + .build(); + LocalityLbEndpoints endpoints = LocalityLbEndpoints.newBuilder() + .setLoadBalancingWeight(UInt32Value.of(10)) + .setPriority(0) + .addLbEndpoints( + LbEndpoint.newBuilder() + .setEndpoint( + Endpoint.newBuilder().setAddress(address).build()) + .setHealthStatus(HealthStatus.HEALTHY) + .build() + ) + .build(); + return ClusterLoadAssignment.newBuilder() + .setClusterName(edsName) + .addEndpoints(endpoints) + .build(); + } +} diff --git a/interop-testing/src/main/java/io/grpc/testing/integration/XdsInteropTest.java b/interop-testing/src/main/java/io/grpc/testing/integration/XdsInteropTest.java new file mode 100644 index 00000000000..410b65d37d9 --- /dev/null +++ b/interop-testing/src/main/java/io/grpc/testing/integration/XdsInteropTest.java @@ -0,0 +1,57 @@ +/* + * Copyright 2021 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.testing.integration; + +import static org.junit.Assert.assertEquals; + +import com.google.protobuf.ByteString; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class XdsInteropTest { + private static final Logger logger = Logger.getLogger(XdsInteropTest.class.getName()); + + /** + * The main application to run test cases. + */ + public static void main(String[] args) throws Exception { + AbstractXdsInteropTest testCase = new PingPong(); + testCase.setUp(); + try { + testCase.run(); + } finally { + testCase.tearDown(); + } + } + + private static class PingPong extends AbstractXdsInteropTest { + @Override + void run() { + Messages.SimpleRequest request = Messages.SimpleRequest.newBuilder() + .setResponseSize(3141) + .setPayload(Messages.Payload.newBuilder() + .setBody(ByteString.copyFrom(new byte[2728]))) + .build(); + Messages.SimpleResponse goldenResponse = Messages.SimpleResponse.newBuilder() + .setPayload(Messages.Payload.newBuilder() + .setBody(ByteString.copyFrom(new byte[3141]))) + .build(); + assertEquals(goldenResponse.getPayload(), blockingStub.unaryCall(request).getPayload()); + logger.log(Level.INFO, "success"); + } + } +} diff --git a/interop-testing/src/main/java/io/grpc/testing/integration/XdsTestControlPlaneService.java b/interop-testing/src/main/java/io/grpc/testing/integration/XdsTestControlPlaneService.java new file mode 100644 index 00000000000..06a4d2467c9 --- /dev/null +++ b/interop-testing/src/main/java/io/grpc/testing/integration/XdsTestControlPlaneService.java @@ -0,0 +1,269 @@ +/* + * Copyright 2021 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.testing.integration; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.protobuf.Any; +import io.grpc.SynchronizationContext; +import io.grpc.stub.StreamObserver; +import io.grpc.xds.shaded.io.envoyproxy.envoy.config.cluster.v3.Cluster; +import io.grpc.xds.shaded.io.envoyproxy.envoy.config.endpoint.v3.ClusterLoadAssignment; +import io.grpc.xds.shaded.io.envoyproxy.envoy.config.listener.v3.Listener; +import io.grpc.xds.shaded.io.envoyproxy.envoy.config.route.v3.RouteConfiguration; +import io.grpc.xds.shaded.io.envoyproxy.envoy.service.discovery.v3.AggregatedDiscoveryServiceGrpc; +import io.grpc.xds.shaded.io.envoyproxy.envoy.service.discovery.v3.DiscoveryRequest; +import io.grpc.xds.shaded.io.envoyproxy.envoy.service.discovery.v3.DiscoveryResponse; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class XdsTestControlPlaneService extends + AggregatedDiscoveryServiceGrpc.AggregatedDiscoveryServiceImplBase { + private static final Logger logger = Logger.getLogger(XdsInteropTest.class.getName()); + + private final SynchronizationContext syncContext = new SynchronizationContext( + new Thread.UncaughtExceptionHandler() { + @Override + public void uncaughtException(Thread t, Throwable e) { + logger.log(Level.SEVERE, "Exception!" + e); + } + }); + + private static final String ADS_TYPE_URL_LDS = + "type.googleapis.com/envoy.config.listener.v3.Listener"; + private static final String ADS_TYPE_URL_RDS = + "type.googleapis.com/envoy.config.route.v3.RouteConfiguration"; + private static final String ADS_TYPE_URL_CDS = + "type.googleapis.com/envoy.config.cluster.v3.Cluster"; + private static final String ADS_TYPE_URL_EDS = + "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment"; + private final ImmutableMap ldsResources; + private final ImmutableMap rdsResources; + private final ImmutableMap cdsResources; + private final ImmutableMap edsResources; + private int ldsVersion = 1; + private int rdsVersion = 1; + private int cdsVersion = 1; + private int edsVersion = 1; + private int ldsNonce = 0; + private int rdsNonce = 0; + private int cdsNonce = 0; + private int edsNonce = 0; + + /** + * Create a control plane service for testing, with static xds configurations. + */ + public XdsTestControlPlaneService(XdsTestControlPlaneConfig config) { + Map ldsMap = new HashMap<>(); + for (Listener apiListener: config.apiListener) { + ldsMap.put(apiListener.getName(), apiListener); + } + for (Listener tcpListener: config.tcpListener) { + ldsMap.put(tcpListener.getName(), tcpListener); + } + this.ldsResources = ImmutableMap.copyOf(ldsMap); + + Map rdsMap = new HashMap<>(); + for (RouteConfiguration rds:config.rds) { + rdsMap.put(rds.getName(), rds); + } + this.rdsResources = ImmutableMap.copyOf(rdsMap); + + Map cdsMap = new HashMap<>(); + for (Cluster cds:config.cds) { + cdsMap.put(cds.getName(), cds); + } + this.cdsResources = ImmutableMap.copyOf(cdsMap); + + Map edsMap = new HashMap<>(); + for (ClusterLoadAssignment eds:config.eds) { + edsMap.put(eds.getClusterName(), eds); + } + this.edsResources = ImmutableMap.copyOf(edsMap); + logger.log(Level.FINER, "control plane config created. " + + "Dumping resources lds:{0},\nrds:{1},\ncds:{2},\neds:{3}", + new Object[]{ldsMap, rdsMap, cdsMap, edsMap}); + } + + public static class XdsTestControlPlaneConfig { + ImmutableList tcpListener; + ImmutableList apiListener; + ImmutableList rds; + ImmutableList cds; + ImmutableList eds; + + /** + * Provides control plane xds configurations. + */ + public XdsTestControlPlaneConfig(List tcpListener, + List apiListener, + List rds, + List cds, + List eds) { + this.tcpListener = ImmutableList.copyOf(tcpListener); + this.apiListener = ImmutableList.copyOf(apiListener); + this.rds = ImmutableList.copyOf(rds); + this.cds = ImmutableList.copyOf(cds); + this.eds = ImmutableList.copyOf(eds); + } + } + + @Override + public StreamObserver streamAggregatedResources( + final StreamObserver responseObserver) { + final StreamObserver requestObserver = + new StreamObserver() { + @Override + public void onNext(final DiscoveryRequest value) { + syncContext.execute(new Runnable() { + @Override + public void run() { + logger.log(Level.FINEST, "control plane received request {0}", value); + if (value.hasErrorDetail()) { + logger.log(Level.FINE, "control plane received nack resource {0}, error {1}", + new Object[]{value.getResourceNamesList(), value.getErrorDetail()}); + return; + } + if (value.getResourceNamesCount() <= 0) { + return; + } + switch (value.getTypeUrl()) { + case ADS_TYPE_URL_LDS: + if (!value.getResponseNonce().isEmpty() + && !String.valueOf(ldsNonce).equals(value.getResponseNonce())) { + logger.log(Level.FINE, "lds resource nonce does not match, ignore."); + return; + } + if (String.valueOf(ldsVersion).equals(value.getVersionInfo())) { + logger.log(Level.FINEST, "control plane received ack for lds resource: {0}", + value.getResourceNamesList()); + return; + } + DiscoveryResponse.Builder responseBuilder = DiscoveryResponse.newBuilder() + .setTypeUrl(ADS_TYPE_URL_LDS) + .setVersionInfo(String.valueOf(ldsVersion++)) + .setNonce(String.valueOf(++ldsNonce)); + for (String ldsName: value.getResourceNamesList()) { + if (ldsResources.containsKey(ldsName)) { + responseBuilder.addResources(Any.pack( + ldsResources.get(ldsName), + ADS_TYPE_URL_LDS + )); + } + } + responseObserver.onNext(responseBuilder.build()); + break; + case ADS_TYPE_URL_RDS: + if (!value.getResponseNonce().isEmpty() + && !String.valueOf(rdsNonce).equals(value.getResponseNonce())) { + logger.log(Level.FINE, "rds resource nonce does not match, ignore."); + return; + } + if (String.valueOf(rdsVersion).equals(value.getVersionInfo())) { + logger.log(Level.FINEST, "control plane received ack for rds resource: {0}", + value.getResourceNamesList()); + return; + } + responseBuilder = DiscoveryResponse.newBuilder() + .setTypeUrl(ADS_TYPE_URL_RDS) + .setVersionInfo(String.valueOf(rdsVersion++)) + .setNonce(String.valueOf(++rdsNonce)); + for (String rdsName: value.getResourceNamesList()) { + if (rdsResources.containsKey(rdsName)) { + responseBuilder.addResources(Any.pack( + rdsResources.get(rdsName), + ADS_TYPE_URL_RDS + )); + } + } + responseObserver.onNext(responseBuilder.build()); + break; + case ADS_TYPE_URL_CDS: + if (!value.getResponseNonce().isEmpty() + && !String.valueOf(cdsNonce).equals(value.getResponseNonce())) { + logger.log(Level.FINE, "cds resource nonce does not match, ignore."); + return; + } + if (String.valueOf(cdsVersion).equals(value.getVersionInfo())) { + logger.log(Level.FINEST, "control plane received ack for cds resource: {0}", + value.getResourceNamesList()); + return; + } + responseBuilder = DiscoveryResponse.newBuilder() + .setTypeUrl(ADS_TYPE_URL_CDS) + .setVersionInfo(String.valueOf(cdsVersion++)) + .setNonce(String.valueOf(++cdsNonce)); + for (String cdsName: value.getResourceNamesList()) { + if (cdsResources.containsKey(cdsName)) { + responseBuilder.addResources(Any.pack( + cdsResources.get(cdsName), + ADS_TYPE_URL_CDS + )); + } + } + responseObserver.onNext(responseBuilder.build()); + break; + case ADS_TYPE_URL_EDS: + if (!value.getResponseNonce().isEmpty() + && !String.valueOf(edsNonce).equals(value.getResponseNonce())) { + logger.log(Level.FINE, "eds resource nonce does not match, ignore."); + return; + } + if (String.valueOf(edsVersion).equals(value.getVersionInfo())) { + logger.log(Level.FINEST, "control plane received ack for eds resource: {0}", + value.getResourceNamesList()); + return; + } + responseBuilder = DiscoveryResponse.newBuilder() + .setTypeUrl(ADS_TYPE_URL_EDS) + .setVersionInfo(String.valueOf(edsVersion++)) + .setNonce(String.valueOf(++edsNonce)); + for (String edsName: value.getResourceNamesList()) { + if (edsResources.containsKey(edsName)) { + responseBuilder.addResources(Any.pack( + edsResources.get(value.getResourceNames(0)), + ADS_TYPE_URL_EDS + )); + } + } + responseObserver.onNext(responseBuilder.build()); + break; + default: + logger.log(Level.WARNING, "unrecognized typeUrl in discoveryRequest: {0}", + value.getTypeUrl()); + } + } + }); + } + + @Override + public void onError(Throwable t) { + logger.log(Level.FINE, "Control plane error: {0} ", t); + } + + @Override + public void onCompleted() { + responseObserver.onCompleted(); + } + }; + return requestObserver; + } +} From bc12a1eb66e00697c6f2d9463ae72019679c150a Mon Sep 17 00:00:00 2001 From: Eric Anderson Date: Wed, 3 Nov 2021 10:16:29 -0700 Subject: [PATCH 19/50] RELEASING.md: Individual OSSRH account is no longer needed Most of the OSSRH interactions are performed by a robot. --- RELEASING.md | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/RELEASING.md b/RELEASING.md index f16cb37c3ad..f2e37f312b4 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -10,19 +10,6 @@ We deploy GRPC to Maven Central under the following systems: Other systems may also work, but we haven't verified them. -Prerequisites -------------- - -### Set Up OSSRH Account - -If you haven't deployed artifacts to Maven Central before, you need to setup -your OSSRH (OSS Repository Hosting) account. -- Follow the instructions on [this - page](https://central.sonatype.org/pages/ossrh-guide.html) to set up an - account with OSSRH. - - You only need to create the account, not set up a new project - - Contact a gRPC maintainer to add your account after you have created it. - Common Variables ---------------- Many of the following commands expect release-specific variables to be set. Set From 93a77a78a9a1ee889bef4c1fcfb02be343c671d1 Mon Sep 17 00:00:00 2001 From: yifeizhuang Date: Wed, 3 Nov 2021 12:13:05 -0700 Subject: [PATCH 20/50] Revert "xds: add xds end to end interop test (#8618)" (#8656) This reverts commit 0000cba665c69958355b639474c7387d98afcc79. --- interop-testing/build.gradle | 8 - .../integration/AbstractXdsInteropTest.java | 337 ------------------ .../testing/integration/XdsInteropTest.java | 57 --- .../XdsTestControlPlaneService.java | 269 -------------- 4 files changed, 671 deletions(-) delete mode 100644 interop-testing/src/main/java/io/grpc/testing/integration/AbstractXdsInteropTest.java delete mode 100644 interop-testing/src/main/java/io/grpc/testing/integration/XdsInteropTest.java delete mode 100644 interop-testing/src/main/java/io/grpc/testing/integration/XdsTestControlPlaneService.java diff --git a/interop-testing/build.gradle b/interop-testing/build.gradle index ef7510c1723..944c0daab81 100644 --- a/interop-testing/build.gradle +++ b/interop-testing/build.gradle @@ -147,13 +147,6 @@ task xds_test_server(type: CreateStartScripts) { classpath = startScripts.classpath } -task xds_e2e_client(type: CreateStartScripts) { - mainClassName = "io.grpc.testing.integration.XdsInteropTest" - applicationName = "xds-e2e-test-client" - outputDir = new File(project.buildDir, 'tmp/scripts/' + name) - classpath = startScripts.classpath -} - applicationDistribution.into("bin") { from(test_client) from(test_server) @@ -164,7 +157,6 @@ applicationDistribution.into("bin") { from(grpclb_fallback_test_client) from(xds_test_client) from(xds_test_server) - from(xds_e2e_client) fileMode = 0755 } diff --git a/interop-testing/src/main/java/io/grpc/testing/integration/AbstractXdsInteropTest.java b/interop-testing/src/main/java/io/grpc/testing/integration/AbstractXdsInteropTest.java deleted file mode 100644 index 0bd2318b0fc..00000000000 --- a/interop-testing/src/main/java/io/grpc/testing/integration/AbstractXdsInteropTest.java +++ /dev/null @@ -1,337 +0,0 @@ -/* - * Copyright 2021 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.testing.integration; - -import com.google.common.collect.ImmutableMap; -import com.google.common.util.concurrent.MoreExecutors; -import com.google.protobuf.Any; -import com.google.protobuf.UInt32Value; -import io.grpc.Grpc; -import io.grpc.InsecureChannelCredentials; -import io.grpc.InsecureServerCredentials; -import io.grpc.ManagedChannel; -import io.grpc.NameResolverRegistry; -import io.grpc.Server; -import io.grpc.netty.NettyServerBuilder; -import io.grpc.xds.XdsNameResolverProvider; -import io.grpc.xds.XdsServerBuilder; -import io.grpc.xds.shaded.io.envoyproxy.envoy.config.cluster.v3.Cluster; -import io.grpc.xds.shaded.io.envoyproxy.envoy.config.core.v3.Address; -import io.grpc.xds.shaded.io.envoyproxy.envoy.config.core.v3.AggregatedConfigSource; -import io.grpc.xds.shaded.io.envoyproxy.envoy.config.core.v3.ConfigSource; -import io.grpc.xds.shaded.io.envoyproxy.envoy.config.core.v3.HealthStatus; -import io.grpc.xds.shaded.io.envoyproxy.envoy.config.core.v3.SocketAddress; -import io.grpc.xds.shaded.io.envoyproxy.envoy.config.core.v3.TrafficDirection; -import io.grpc.xds.shaded.io.envoyproxy.envoy.config.endpoint.v3.ClusterLoadAssignment; -import io.grpc.xds.shaded.io.envoyproxy.envoy.config.endpoint.v3.Endpoint; -import io.grpc.xds.shaded.io.envoyproxy.envoy.config.endpoint.v3.LbEndpoint; -import io.grpc.xds.shaded.io.envoyproxy.envoy.config.endpoint.v3.LocalityLbEndpoints; -import io.grpc.xds.shaded.io.envoyproxy.envoy.config.listener.v3.ApiListener; -import io.grpc.xds.shaded.io.envoyproxy.envoy.config.listener.v3.Filter; -import io.grpc.xds.shaded.io.envoyproxy.envoy.config.listener.v3.FilterChain; -import io.grpc.xds.shaded.io.envoyproxy.envoy.config.listener.v3.FilterChainMatch; -import io.grpc.xds.shaded.io.envoyproxy.envoy.config.listener.v3.Listener; -import io.grpc.xds.shaded.io.envoyproxy.envoy.config.route.v3.NonForwardingAction; -import io.grpc.xds.shaded.io.envoyproxy.envoy.config.route.v3.Route; -import io.grpc.xds.shaded.io.envoyproxy.envoy.config.route.v3.RouteAction; -import io.grpc.xds.shaded.io.envoyproxy.envoy.config.route.v3.RouteConfiguration; -import io.grpc.xds.shaded.io.envoyproxy.envoy.config.route.v3.RouteMatch; -import io.grpc.xds.shaded.io.envoyproxy.envoy.config.route.v3.VirtualHost; -import io.grpc.xds.shaded.io.envoyproxy.envoy.extensions.filters.http.router.v3.Router; -import io.grpc.xds.shaded.io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager; -import io.grpc.xds.shaded.io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.HttpFilter; -import io.grpc.xds.shaded.io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.Rds; - -import java.util.Collections; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * Abstract base class for end-to-end xds tests. - * A local control plane is implemented in {@link XdsTestControlPlaneService}. - * Test cases can inject xds configs to the control plane for testing. - */ -public abstract class AbstractXdsInteropTest { - private static final Logger logger = Logger.getLogger(AbstractXdsInteropTest.class.getName()); - - protected static final int testServerPort = 8080; - private static final int controlPlaneServicePort = 443; - private Server server; - private Server controlPlane; - protected TestServiceGrpc.TestServiceBlockingStub blockingStub; - private ScheduledExecutorService executor; - private XdsNameResolverProvider nameResolverProvider; - private static final String scheme = "test-xds"; - private static final String serverHostName = "0.0.0.0:" + testServerPort; - private static final String SERVER_LISTENER_TEMPLATE = - "grpc/server?udpa.resource.listening_address=%s"; - private static final String rdsName = "route-config.googleapis.com"; - private static final String clusterName = "cluster0"; - private static final String edsName = "eds-service-0"; - private static final String HTTP_CONNECTION_MANAGER_TYPE_URL = - "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3" - + ".HttpConnectionManager"; - - private static final Map defaultClientBootstrapOverride = ImmutableMap.of( - "node", ImmutableMap.of( - "id", UUID.randomUUID().toString(), - "cluster", "cluster0"), - "xds_servers", Collections.singletonList( - ImmutableMap.of( - "server_uri", "localhost:" + controlPlaneServicePort, - "channel_creds", Collections.singletonList( - ImmutableMap.of("type", "insecure") - ), - "server_features", Collections.singletonList("xds_v3") - ) - ) - ); - - /** - * Provides default client bootstrap. - * A subclass test case should override this method if it tests client bootstrap. - */ - protected Map getClientBootstrapOverride() { - return defaultClientBootstrapOverride; - } - - private static final Map defaultServerBootstrapOverride = ImmutableMap.of( - "node", ImmutableMap.of( - "id", UUID.randomUUID().toString()), - "xds_servers", Collections.singletonList( - ImmutableMap.of( - "server_uri", "localhost:" + controlPlaneServicePort, - "channel_creds", Collections.singletonList( - ImmutableMap.of("type", "insecure") - ), - "server_features", Collections.singletonList("xds_v3") - ) - ), - "server_listener_resource_name_template", SERVER_LISTENER_TEMPLATE - ); - - /** - * Provides default server bootstrap. - * A subclass test case should override this method if it tests server bootstrap. - */ - protected Map getServerBootstrapOverride() { - return defaultServerBootstrapOverride; - } - - protected void setUp() throws Exception { - startControlPlane(); - startServer(); - nameResolverProvider = XdsNameResolverProvider.createForTest(scheme, - getClientBootstrapOverride()); - NameResolverRegistry.getDefaultRegistry().register(nameResolverProvider); - ManagedChannel channel = Grpc.newChannelBuilder(scheme + ":///" + serverHostName, - InsecureChannelCredentials.create()).build(); - blockingStub = TestServiceGrpc.newBlockingStub(channel); - } - - protected void tearDown() throws Exception { - if (server != null) { - server.shutdownNow(); - if (!server.awaitTermination(5, TimeUnit.SECONDS)) { - logger.log(Level.SEVERE, "Timed out waiting for server shutdown"); - } - } - if (controlPlane != null) { - controlPlane.shutdownNow(); - if (!controlPlane.awaitTermination(5, TimeUnit.SECONDS)) { - logger.log(Level.SEVERE, "Timed out waiting for server shutdown"); - } - } - if (executor != null) { - MoreExecutors.shutdownAndAwaitTermination(executor, 5, TimeUnit.SECONDS); - } - NameResolverRegistry.getDefaultRegistry().deregister(nameResolverProvider); - } - - protected void startServer() throws Exception { - executor = Executors.newSingleThreadScheduledExecutor(); - XdsServerBuilder serverBuilder = XdsServerBuilder.forPort( - testServerPort, InsecureServerCredentials.create()) - .addService(new TestServiceImpl(executor)) - .overrideBootstrapForTest(getServerBootstrapOverride()); - server = serverBuilder.build().start(); - } - - /** - * Provides default control plane xds configs. - * A subclass test case should override this method to inject control plane xds configs to verify - * end-to-end behavior. - */ - protected XdsTestControlPlaneService.XdsTestControlPlaneConfig getControlPlaneConfig() { - String tcpListenerName = SERVER_LISTENER_TEMPLATE.replaceAll("%s", serverHostName); - return new XdsTestControlPlaneService.XdsTestControlPlaneConfig( - Collections.singletonList(serverListener(tcpListenerName, serverHostName)), - Collections.singletonList(clientListener(serverHostName)), - Collections.singletonList(rds(serverHostName)), - Collections.singletonList(cds()), - Collections.singletonList(eds(testServerPort)) - ); - } - - private void startControlPlane() throws Exception { - XdsTestControlPlaneService.XdsTestControlPlaneConfig controlPlaneConfig = - getControlPlaneConfig(); - logger.log(Level.FINER, "Starting control plane with config: {0}", controlPlaneConfig); - XdsTestControlPlaneService controlPlaneService = new XdsTestControlPlaneService( - controlPlaneConfig); - NettyServerBuilder controlPlaneServerBuilder = - NettyServerBuilder.forPort(controlPlaneServicePort) - .addService(controlPlaneService); - controlPlane = controlPlaneServerBuilder.build().start(); - } - - /** - * A subclass test case should override this method to verify end-to-end behaviour. - */ - abstract void run(); - - private static Listener clientListener(String name) { - HttpFilter httpFilter = HttpFilter.newBuilder() - .setName("terminal-filter") - .setTypedConfig(Any.pack(Router.newBuilder().build())) - .setIsOptional(true) - .build(); - ApiListener apiListener = ApiListener.newBuilder().setApiListener(Any.pack( - HttpConnectionManager.newBuilder() - .setRds( - Rds.newBuilder() - .setRouteConfigName(rdsName) - .setConfigSource( - ConfigSource.newBuilder() - .setAds(AggregatedConfigSource.getDefaultInstance()))) - .addAllHttpFilters(Collections.singletonList(httpFilter)) - .build(), - HTTP_CONNECTION_MANAGER_TYPE_URL) - ).build(); - Listener listener = Listener.newBuilder() - .setName(name) - .setApiListener(apiListener).build(); - return listener; - } - - private static Listener serverListener(String name, String authority) { - HttpFilter routerFilter = HttpFilter.newBuilder() - .setName("terminal-filter") - .setTypedConfig( - Any.pack(Router.newBuilder().build())) - .setIsOptional(true) - .build(); - VirtualHost virtualHost = VirtualHost.newBuilder() - .setName("virtual-host-0") - .addDomains(authority) - .addRoutes( - Route.newBuilder() - .setMatch( - RouteMatch.newBuilder().setPrefix("/").build() - ) - .setNonForwardingAction(NonForwardingAction.newBuilder().build()) - .build() - ).build(); - RouteConfiguration routeConfig = RouteConfiguration.newBuilder() - .addVirtualHosts(virtualHost) - .build(); - Filter filter = Filter.newBuilder() - .setName("network-filter-0") - .setTypedConfig( - Any.pack( - HttpConnectionManager.newBuilder() - .setRouteConfig(routeConfig) - .addAllHttpFilters(Collections.singletonList(routerFilter)) - .build() - ) - ).build(); - FilterChainMatch filterChainMatch = FilterChainMatch.newBuilder() - .setSourceType(FilterChainMatch.ConnectionSourceType.ANY) - .build(); - FilterChain filterChain = FilterChain.newBuilder() - .setName("filter-chain-0") - .setFilterChainMatch(filterChainMatch) - .addFilters(filter) - .build(); - return Listener.newBuilder() - .setName(name) - .setTrafficDirection(TrafficDirection.INBOUND) - .addFilterChains(filterChain) - .build(); - } - - private static RouteConfiguration rds(String authority) { - VirtualHost virtualHost = VirtualHost.newBuilder() - .addDomains(authority) - .addRoutes( - Route.newBuilder() - .setMatch( - RouteMatch.newBuilder().setPrefix("/").build() - ) - .setRoute( - RouteAction.newBuilder().setCluster(clusterName).build() - ) - .build()) - .build(); - return RouteConfiguration.newBuilder().setName(rdsName).addVirtualHosts(virtualHost).build(); - } - - private static Cluster cds() { - return Cluster.newBuilder() - .setName(clusterName) - .setType(Cluster.DiscoveryType.EDS) - .setEdsClusterConfig( - Cluster.EdsClusterConfig.newBuilder() - .setServiceName(edsName) - .setEdsConfig( - ConfigSource.newBuilder() - .setAds(AggregatedConfigSource.newBuilder().build()) - .build()) - .build() - ) - .setLbPolicy(Cluster.LbPolicy.ROUND_ROBIN) - .build(); - } - - private static ClusterLoadAssignment eds(int port) { - Address address = Address.newBuilder() - .setSocketAddress( - SocketAddress.newBuilder().setAddress("0.0.0.0").setPortValue(port).build() - ) - .build(); - LocalityLbEndpoints endpoints = LocalityLbEndpoints.newBuilder() - .setLoadBalancingWeight(UInt32Value.of(10)) - .setPriority(0) - .addLbEndpoints( - LbEndpoint.newBuilder() - .setEndpoint( - Endpoint.newBuilder().setAddress(address).build()) - .setHealthStatus(HealthStatus.HEALTHY) - .build() - ) - .build(); - return ClusterLoadAssignment.newBuilder() - .setClusterName(edsName) - .addEndpoints(endpoints) - .build(); - } -} diff --git a/interop-testing/src/main/java/io/grpc/testing/integration/XdsInteropTest.java b/interop-testing/src/main/java/io/grpc/testing/integration/XdsInteropTest.java deleted file mode 100644 index 410b65d37d9..00000000000 --- a/interop-testing/src/main/java/io/grpc/testing/integration/XdsInteropTest.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2021 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.testing.integration; - -import static org.junit.Assert.assertEquals; - -import com.google.protobuf.ByteString; -import java.util.logging.Level; -import java.util.logging.Logger; - -public class XdsInteropTest { - private static final Logger logger = Logger.getLogger(XdsInteropTest.class.getName()); - - /** - * The main application to run test cases. - */ - public static void main(String[] args) throws Exception { - AbstractXdsInteropTest testCase = new PingPong(); - testCase.setUp(); - try { - testCase.run(); - } finally { - testCase.tearDown(); - } - } - - private static class PingPong extends AbstractXdsInteropTest { - @Override - void run() { - Messages.SimpleRequest request = Messages.SimpleRequest.newBuilder() - .setResponseSize(3141) - .setPayload(Messages.Payload.newBuilder() - .setBody(ByteString.copyFrom(new byte[2728]))) - .build(); - Messages.SimpleResponse goldenResponse = Messages.SimpleResponse.newBuilder() - .setPayload(Messages.Payload.newBuilder() - .setBody(ByteString.copyFrom(new byte[3141]))) - .build(); - assertEquals(goldenResponse.getPayload(), blockingStub.unaryCall(request).getPayload()); - logger.log(Level.INFO, "success"); - } - } -} diff --git a/interop-testing/src/main/java/io/grpc/testing/integration/XdsTestControlPlaneService.java b/interop-testing/src/main/java/io/grpc/testing/integration/XdsTestControlPlaneService.java deleted file mode 100644 index 06a4d2467c9..00000000000 --- a/interop-testing/src/main/java/io/grpc/testing/integration/XdsTestControlPlaneService.java +++ /dev/null @@ -1,269 +0,0 @@ -/* - * Copyright 2021 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.testing.integration; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.protobuf.Any; -import io.grpc.SynchronizationContext; -import io.grpc.stub.StreamObserver; -import io.grpc.xds.shaded.io.envoyproxy.envoy.config.cluster.v3.Cluster; -import io.grpc.xds.shaded.io.envoyproxy.envoy.config.endpoint.v3.ClusterLoadAssignment; -import io.grpc.xds.shaded.io.envoyproxy.envoy.config.listener.v3.Listener; -import io.grpc.xds.shaded.io.envoyproxy.envoy.config.route.v3.RouteConfiguration; -import io.grpc.xds.shaded.io.envoyproxy.envoy.service.discovery.v3.AggregatedDiscoveryServiceGrpc; -import io.grpc.xds.shaded.io.envoyproxy.envoy.service.discovery.v3.DiscoveryRequest; -import io.grpc.xds.shaded.io.envoyproxy.envoy.service.discovery.v3.DiscoveryResponse; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; - -public class XdsTestControlPlaneService extends - AggregatedDiscoveryServiceGrpc.AggregatedDiscoveryServiceImplBase { - private static final Logger logger = Logger.getLogger(XdsInteropTest.class.getName()); - - private final SynchronizationContext syncContext = new SynchronizationContext( - new Thread.UncaughtExceptionHandler() { - @Override - public void uncaughtException(Thread t, Throwable e) { - logger.log(Level.SEVERE, "Exception!" + e); - } - }); - - private static final String ADS_TYPE_URL_LDS = - "type.googleapis.com/envoy.config.listener.v3.Listener"; - private static final String ADS_TYPE_URL_RDS = - "type.googleapis.com/envoy.config.route.v3.RouteConfiguration"; - private static final String ADS_TYPE_URL_CDS = - "type.googleapis.com/envoy.config.cluster.v3.Cluster"; - private static final String ADS_TYPE_URL_EDS = - "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment"; - private final ImmutableMap ldsResources; - private final ImmutableMap rdsResources; - private final ImmutableMap cdsResources; - private final ImmutableMap edsResources; - private int ldsVersion = 1; - private int rdsVersion = 1; - private int cdsVersion = 1; - private int edsVersion = 1; - private int ldsNonce = 0; - private int rdsNonce = 0; - private int cdsNonce = 0; - private int edsNonce = 0; - - /** - * Create a control plane service for testing, with static xds configurations. - */ - public XdsTestControlPlaneService(XdsTestControlPlaneConfig config) { - Map ldsMap = new HashMap<>(); - for (Listener apiListener: config.apiListener) { - ldsMap.put(apiListener.getName(), apiListener); - } - for (Listener tcpListener: config.tcpListener) { - ldsMap.put(tcpListener.getName(), tcpListener); - } - this.ldsResources = ImmutableMap.copyOf(ldsMap); - - Map rdsMap = new HashMap<>(); - for (RouteConfiguration rds:config.rds) { - rdsMap.put(rds.getName(), rds); - } - this.rdsResources = ImmutableMap.copyOf(rdsMap); - - Map cdsMap = new HashMap<>(); - for (Cluster cds:config.cds) { - cdsMap.put(cds.getName(), cds); - } - this.cdsResources = ImmutableMap.copyOf(cdsMap); - - Map edsMap = new HashMap<>(); - for (ClusterLoadAssignment eds:config.eds) { - edsMap.put(eds.getClusterName(), eds); - } - this.edsResources = ImmutableMap.copyOf(edsMap); - logger.log(Level.FINER, "control plane config created. " - + "Dumping resources lds:{0},\nrds:{1},\ncds:{2},\neds:{3}", - new Object[]{ldsMap, rdsMap, cdsMap, edsMap}); - } - - public static class XdsTestControlPlaneConfig { - ImmutableList tcpListener; - ImmutableList apiListener; - ImmutableList rds; - ImmutableList cds; - ImmutableList eds; - - /** - * Provides control plane xds configurations. - */ - public XdsTestControlPlaneConfig(List tcpListener, - List apiListener, - List rds, - List cds, - List eds) { - this.tcpListener = ImmutableList.copyOf(tcpListener); - this.apiListener = ImmutableList.copyOf(apiListener); - this.rds = ImmutableList.copyOf(rds); - this.cds = ImmutableList.copyOf(cds); - this.eds = ImmutableList.copyOf(eds); - } - } - - @Override - public StreamObserver streamAggregatedResources( - final StreamObserver responseObserver) { - final StreamObserver requestObserver = - new StreamObserver() { - @Override - public void onNext(final DiscoveryRequest value) { - syncContext.execute(new Runnable() { - @Override - public void run() { - logger.log(Level.FINEST, "control plane received request {0}", value); - if (value.hasErrorDetail()) { - logger.log(Level.FINE, "control plane received nack resource {0}, error {1}", - new Object[]{value.getResourceNamesList(), value.getErrorDetail()}); - return; - } - if (value.getResourceNamesCount() <= 0) { - return; - } - switch (value.getTypeUrl()) { - case ADS_TYPE_URL_LDS: - if (!value.getResponseNonce().isEmpty() - && !String.valueOf(ldsNonce).equals(value.getResponseNonce())) { - logger.log(Level.FINE, "lds resource nonce does not match, ignore."); - return; - } - if (String.valueOf(ldsVersion).equals(value.getVersionInfo())) { - logger.log(Level.FINEST, "control plane received ack for lds resource: {0}", - value.getResourceNamesList()); - return; - } - DiscoveryResponse.Builder responseBuilder = DiscoveryResponse.newBuilder() - .setTypeUrl(ADS_TYPE_URL_LDS) - .setVersionInfo(String.valueOf(ldsVersion++)) - .setNonce(String.valueOf(++ldsNonce)); - for (String ldsName: value.getResourceNamesList()) { - if (ldsResources.containsKey(ldsName)) { - responseBuilder.addResources(Any.pack( - ldsResources.get(ldsName), - ADS_TYPE_URL_LDS - )); - } - } - responseObserver.onNext(responseBuilder.build()); - break; - case ADS_TYPE_URL_RDS: - if (!value.getResponseNonce().isEmpty() - && !String.valueOf(rdsNonce).equals(value.getResponseNonce())) { - logger.log(Level.FINE, "rds resource nonce does not match, ignore."); - return; - } - if (String.valueOf(rdsVersion).equals(value.getVersionInfo())) { - logger.log(Level.FINEST, "control plane received ack for rds resource: {0}", - value.getResourceNamesList()); - return; - } - responseBuilder = DiscoveryResponse.newBuilder() - .setTypeUrl(ADS_TYPE_URL_RDS) - .setVersionInfo(String.valueOf(rdsVersion++)) - .setNonce(String.valueOf(++rdsNonce)); - for (String rdsName: value.getResourceNamesList()) { - if (rdsResources.containsKey(rdsName)) { - responseBuilder.addResources(Any.pack( - rdsResources.get(rdsName), - ADS_TYPE_URL_RDS - )); - } - } - responseObserver.onNext(responseBuilder.build()); - break; - case ADS_TYPE_URL_CDS: - if (!value.getResponseNonce().isEmpty() - && !String.valueOf(cdsNonce).equals(value.getResponseNonce())) { - logger.log(Level.FINE, "cds resource nonce does not match, ignore."); - return; - } - if (String.valueOf(cdsVersion).equals(value.getVersionInfo())) { - logger.log(Level.FINEST, "control plane received ack for cds resource: {0}", - value.getResourceNamesList()); - return; - } - responseBuilder = DiscoveryResponse.newBuilder() - .setTypeUrl(ADS_TYPE_URL_CDS) - .setVersionInfo(String.valueOf(cdsVersion++)) - .setNonce(String.valueOf(++cdsNonce)); - for (String cdsName: value.getResourceNamesList()) { - if (cdsResources.containsKey(cdsName)) { - responseBuilder.addResources(Any.pack( - cdsResources.get(cdsName), - ADS_TYPE_URL_CDS - )); - } - } - responseObserver.onNext(responseBuilder.build()); - break; - case ADS_TYPE_URL_EDS: - if (!value.getResponseNonce().isEmpty() - && !String.valueOf(edsNonce).equals(value.getResponseNonce())) { - logger.log(Level.FINE, "eds resource nonce does not match, ignore."); - return; - } - if (String.valueOf(edsVersion).equals(value.getVersionInfo())) { - logger.log(Level.FINEST, "control plane received ack for eds resource: {0}", - value.getResourceNamesList()); - return; - } - responseBuilder = DiscoveryResponse.newBuilder() - .setTypeUrl(ADS_TYPE_URL_EDS) - .setVersionInfo(String.valueOf(edsVersion++)) - .setNonce(String.valueOf(++edsNonce)); - for (String edsName: value.getResourceNamesList()) { - if (edsResources.containsKey(edsName)) { - responseBuilder.addResources(Any.pack( - edsResources.get(value.getResourceNames(0)), - ADS_TYPE_URL_EDS - )); - } - } - responseObserver.onNext(responseBuilder.build()); - break; - default: - logger.log(Level.WARNING, "unrecognized typeUrl in discoveryRequest: {0}", - value.getTypeUrl()); - } - } - }); - } - - @Override - public void onError(Throwable t) { - logger.log(Level.FINE, "Control plane error: {0} ", t); - } - - @Override - public void onCompleted() { - responseObserver.onCompleted(); - } - }; - return requestObserver; - } -} From efec994f4cfa0a7536a815629d11bc70668fb434 Mon Sep 17 00:00:00 2001 From: Sergii Tkachenko Date: Thu, 4 Nov 2021 14:10:03 -0400 Subject: [PATCH 21/50] Update MAINTAINERS.md --- MAINTAINERS.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/MAINTAINERS.md b/MAINTAINERS.md index 5426f83f90b..f657dccc405 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -8,23 +8,24 @@ See [CONTRIBUTING.md](https://github.com/grpc/grpc-community/blob/master/CONTRIB for general contribution guidelines. ## Maintainers (in alphabetical order) - - [dapengzhang0](https://github.com/dapengzhang0), Google LLC - [ejona86](https://github.com/ejona86), Google LLC -- [ericgribkoff](https://github.com/ericgribkoff), Google LLC - [ran-su](https://github.com/ran-su), Google LLC - [sanjaypujare](https://github.com/sanjaypujare), Google LLC - [sergiitk](https://github.com/sergiitk), Google LLC - [srini100](https://github.com/srini100), Google LLC -- [voidzcy](https://github.com/voidzcy), Google LLC +- [temawi](https://github.com/temawi), Google LLC +- [YifeiZhuang](https://github.com/YifeiZhuang), Google LLC - [zhangkun83](https://github.com/zhangkun83), Google LLC ## Emeritus Maintainers (in alphabetical order) - [carl-mastrangelo](https://github.com/carl-mastrangelo), Google LLC - [creamsoup](https://github.com/creamsoup), Google LLC +- [ericgribkoff](https://github.com/ericgribkoff), Google LLC - [jiangtaoli2016](https://github.com/jiangtaoli2016), Google LLC - [jtattermusch](https://github.com/jtattermusch), Google LLC - [louiscryan](https://github.com/louiscryan), Google LLC - [nicolasnoble](https://github.com/nicolasnoble), Google LLC - [nmittler](https://github.com/nmittler), Google LLC +- [voidzcy](https://github.com/voidzcy), Google LLC - [zpencer](https://github.com/zpencer), Google LLC From c0b8eff98409726860fe9a28339c30669e5571f8 Mon Sep 17 00:00:00 2001 From: sanjaypujare Date: Thu, 4 Nov 2021 12:31:11 -0700 Subject: [PATCH 22/50] interop-testing: assign server as soon as it is built instead of after start (#8662) --- .../io/grpc/testing/integration/XdsTestServer.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/interop-testing/src/main/java/io/grpc/testing/integration/XdsTestServer.java b/interop-testing/src/main/java/io/grpc/testing/integration/XdsTestServer.java index b2af083bd24..6b3e7213cfb 100644 --- a/interop-testing/src/main/java/io/grpc/testing/integration/XdsTestServer.java +++ b/interop-testing/src/main/java/io/grpc/testing/integration/XdsTestServer.java @@ -180,16 +180,16 @@ private void start() throws Exception { .addService( ServerInterceptors.intercept( new TestServiceImpl(serverId, host), new TestInfoInterceptor(host))) - .build() - .start(); + .build(); + server.start(); maintenanceServer = NettyServerBuilder.forPort(maintenancePort) .addService(new XdsUpdateHealthServiceImpl(health)) .addService(health.getHealthService()) .addService(ProtoReflectionService.newInstance()) .addServices(AdminInterface.getStandardServices()) - .build() - .start(); + .build(); + maintenanceServer.start(); } else { server = NettyServerBuilder.forPort(port) @@ -200,8 +200,8 @@ private void start() throws Exception { .addService(health.getHealthService()) .addService(ProtoReflectionService.newInstance()) .addServices(AdminInterface.getStandardServices()) - .build() - .start(); + .build(); + server.start(); maintenanceServer = null; } health.setStatus("", ServingStatus.SERVING); From a5c526c12f699af9a846d9faabc18038ef431675 Mon Sep 17 00:00:00 2001 From: yifeizhuang Date: Thu, 4 Nov 2021 14:10:03 -0700 Subject: [PATCH 23/50] xds: remove filter chain uuid name generator (#8663) Generating a uuid in filterChain breaks the de-duplication detection which causes XdsServer to cycle connections, so removing it. An empty name is now allowed. The name is currently only used for debug purpose. --- xds/src/main/java/io/grpc/xds/ClientXdsClient.java | 7 +------ xds/src/main/java/io/grpc/xds/EnvoyServerProtoData.java | 2 +- xds/src/test/java/io/grpc/xds/ClientXdsClientDataTest.java | 4 ++-- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/xds/src/main/java/io/grpc/xds/ClientXdsClient.java b/xds/src/main/java/io/grpc/xds/ClientXdsClient.java index 92b1cfd3f73..c4fedea4aa4 100644 --- a/xds/src/main/java/io/grpc/xds/ClientXdsClient.java +++ b/xds/src/main/java/io/grpc/xds/ClientXdsClient.java @@ -112,7 +112,6 @@ import java.util.Map; import java.util.Objects; import java.util.Set; -import java.util.UUID; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import javax.annotation.Nullable; @@ -441,14 +440,10 @@ static FilterChain parseFilterChain( validateDownstreamTlsContext(downstreamTlsContextProto, certProviderInstances)); } - String name = proto.getName(); - if (name.isEmpty()) { - name = UUID.randomUUID().toString(); - } FilterChainMatch filterChainMatch = parseFilterChainMatch(proto.getFilterChainMatch()); checkForUniqueness(uniqueSet, filterChainMatch); return new FilterChain( - name, + proto.getName(), filterChainMatch, httpConnectionManager, downstreamTlsContext, diff --git a/xds/src/main/java/io/grpc/xds/EnvoyServerProtoData.java b/xds/src/main/java/io/grpc/xds/EnvoyServerProtoData.java index aa53d834d3b..09318a8c150 100644 --- a/xds/src/main/java/io/grpc/xds/EnvoyServerProtoData.java +++ b/xds/src/main/java/io/grpc/xds/EnvoyServerProtoData.java @@ -314,7 +314,7 @@ public String toString() { * Corresponds to Envoy proto message {@link io.envoyproxy.envoy.api.v2.listener.FilterChain}. */ static final class FilterChain { - // Unique name for the FilterChain. + // possibly empty private final String name; // TODO(sanjaypujare): flatten structure by moving FilterChainMatch class members here. private final FilterChainMatch filterChainMatch; diff --git a/xds/src/test/java/io/grpc/xds/ClientXdsClientDataTest.java b/xds/src/test/java/io/grpc/xds/ClientXdsClientDataTest.java index 0207c2b94e5..2dbab1bcb9d 100644 --- a/xds/src/test/java/io/grpc/xds/ClientXdsClientDataTest.java +++ b/xds/src/test/java/io/grpc/xds/ClientXdsClientDataTest.java @@ -1587,7 +1587,7 @@ public void parseFilterChain_unsupportedFilter() throws ResourceInvalidException } @Test - public void parseFilterChain_noName_generatedUuid() throws ResourceInvalidException { + public void parseFilterChain_noName() throws ResourceInvalidException { FilterChain filterChain1 = FilterChain.newBuilder() .setFilterChainMatch(FilterChainMatch.getDefaultInstance()) @@ -1615,7 +1615,7 @@ public void parseFilterChain_noName_generatedUuid() throws ResourceInvalidExcept EnvoyServerProtoData.FilterChain parsedFilterChain2 = ClientXdsClient.parseFilterChain( filterChain2, new HashSet(), null, filterRegistry, null, null, true /* does not matter */); - assertThat(parsedFilterChain1.getName()).isNotEqualTo(parsedFilterChain2.getName()); + assertThat(parsedFilterChain1.getName()).isEqualTo(parsedFilterChain2.getName()); } @Test From 0d34572149b515c19130800bb593c8f3985c5e21 Mon Sep 17 00:00:00 2001 From: Eric Anderson Date: Thu, 4 Nov 2021 15:29:24 -0700 Subject: [PATCH 24/50] kokoro: Enable xds authz_test It is successfully passing against prod: https://source.cloud.google.com/results/invocations/e2be0996-ed4d-4a4c-90ad-20bc706f9f70/targets --- buildscripts/kokoro/xds-k8s.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/buildscripts/kokoro/xds-k8s.sh b/buildscripts/kokoro/xds-k8s.sh index d0275d459e3..105e67b2d0f 100755 --- a/buildscripts/kokoro/xds-k8s.sh +++ b/buildscripts/kokoro/xds-k8s.sh @@ -168,6 +168,7 @@ main() { cd "${TEST_DRIVER_FULL_DIR}" run_test baseline_test run_test security_test + run_test authz_test } main "$@" From d548a35ab975106a84a2870b406272b34034cce7 Mon Sep 17 00:00:00 2001 From: Sergii Tkachenko Date: Wed, 3 Nov 2021 17:04:11 -0700 Subject: [PATCH 25/50] Update README etc to reference 1.42.0 --- README.md | 30 ++++++++++++------------ cronet/README.md | 2 +- documentation/android-channel-builder.md | 4 ++-- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index ea11cbde260..aa3c61b9c47 100644 --- a/README.md +++ b/README.md @@ -31,8 +31,8 @@ For a guided tour, take a look at the [quick start guide](https://grpc.io/docs/languages/java/quickstart) or the more explanatory [gRPC basics](https://grpc.io/docs/languages/java/basics). -The [examples](https://github.com/grpc/grpc-java/tree/v1.41.0/examples) and the -[Android example](https://github.com/grpc/grpc-java/tree/v1.41.0/examples/android) +The [examples](https://github.com/grpc/grpc-java/tree/v1.42.0/examples) and the +[Android example](https://github.com/grpc/grpc-java/tree/v1.42.0/examples/android) are standalone projects that showcase the usage of gRPC. Download @@ -43,17 +43,17 @@ Download [the JARs][]. Or for Maven with non-Android, add to your `pom.xml`: io.grpc grpc-netty-shaded - 1.41.0 + 1.42.0 io.grpc grpc-protobuf - 1.41.0 + 1.42.0 io.grpc grpc-stub - 1.41.0 + 1.42.0 org.apache.tomcat @@ -65,23 +65,23 @@ Download [the JARs][]. Or for Maven with non-Android, add to your `pom.xml`: Or for Gradle with non-Android, add to your dependencies: ```gradle -implementation 'io.grpc:grpc-netty-shaded:1.41.0' -implementation 'io.grpc:grpc-protobuf:1.41.0' -implementation 'io.grpc:grpc-stub:1.41.0' +implementation 'io.grpc:grpc-netty-shaded:1.42.0' +implementation 'io.grpc:grpc-protobuf:1.42.0' +implementation 'io.grpc:grpc-stub:1.42.0' compileOnly 'org.apache.tomcat:annotations-api:6.0.53' // necessary for Java 9+ ``` For Android client, use `grpc-okhttp` instead of `grpc-netty-shaded` and `grpc-protobuf-lite` instead of `grpc-protobuf`: ```gradle -implementation 'io.grpc:grpc-okhttp:1.41.0' -implementation 'io.grpc:grpc-protobuf-lite:1.41.0' -implementation 'io.grpc:grpc-stub:1.41.0' +implementation 'io.grpc:grpc-okhttp:1.42.0' +implementation 'io.grpc:grpc-protobuf-lite:1.42.0' +implementation 'io.grpc:grpc-stub:1.42.0' compileOnly 'org.apache.tomcat:annotations-api:6.0.53' // necessary for Java 9+ ``` [the JARs]: -https://search.maven.org/search?q=g:io.grpc%20AND%20v:1.41.0 +https://search.maven.org/search?q=g:io.grpc%20AND%20v:1.42.0 Development snapshots are available in [Sonatypes's snapshot repository](https://oss.sonatype.org/content/repositories/snapshots/). @@ -113,7 +113,7 @@ For protobuf-based codegen integrated with the Maven build system, you can use com.google.protobuf:protoc:3.17.3:exe:${os.detected.classifier} grpc-java - io.grpc:protoc-gen-grpc-java:1.41.0:exe:${os.detected.classifier} + io.grpc:protoc-gen-grpc-java:1.42.0:exe:${os.detected.classifier} @@ -143,7 +143,7 @@ protobuf { } plugins { grpc { - artifact = 'io.grpc:protoc-gen-grpc-java:1.41.0' + artifact = 'io.grpc:protoc-gen-grpc-java:1.42.0' } } generateProtoTasks { @@ -176,7 +176,7 @@ protobuf { } plugins { grpc { - artifact = 'io.grpc:protoc-gen-grpc-java:1.41.0' + artifact = 'io.grpc:protoc-gen-grpc-java:1.42.0' } } generateProtoTasks { diff --git a/cronet/README.md b/cronet/README.md index 8b220bd606d..9a9f8fbe6c4 100644 --- a/cronet/README.md +++ b/cronet/README.md @@ -26,7 +26,7 @@ In your app module's `build.gradle` file, include a dependency on both `grpc-cro Google Play Services Client Library for Cronet ``` -implementation 'io.grpc:grpc-cronet:1.41.0' +implementation 'io.grpc:grpc-cronet:1.42.0' implementation 'com.google.android.gms:play-services-cronet:16.0.0' ``` diff --git a/documentation/android-channel-builder.md b/documentation/android-channel-builder.md index 60e3bb35a85..7e56e391038 100644 --- a/documentation/android-channel-builder.md +++ b/documentation/android-channel-builder.md @@ -36,8 +36,8 @@ In your `build.gradle` file, include a dependency on both `grpc-android` and `grpc-okhttp`: ``` -implementation 'io.grpc:grpc-android:1.41.0' -implementation 'io.grpc:grpc-okhttp:1.41.0' +implementation 'io.grpc:grpc-android:1.42.0' +implementation 'io.grpc:grpc-okhttp:1.42.0' ``` You also need permission to access the device's network state in your From fe9026ed8ae5025e92d452e077bfa0b329c123a0 Mon Sep 17 00:00:00 2001 From: Eric Anderson Date: Fri, 5 Nov 2021 09:16:49 -0700 Subject: [PATCH 26/50] kokoro: Increase xds-k8s timeout to 3 hours The addition of the authz tests in 0d345721 is causing the tests to exceed their timeout. By itself, the authz test takes about an hour in this environment. Before the authz tests, xds-k8s was taking an hour and a half. --- buildscripts/kokoro/xds-k8s.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildscripts/kokoro/xds-k8s.cfg b/buildscripts/kokoro/xds-k8s.cfg index 09a8e705a4d..bdbd968d7a3 100644 --- a/buildscripts/kokoro/xds-k8s.cfg +++ b/buildscripts/kokoro/xds-k8s.cfg @@ -2,7 +2,7 @@ # Location of the continuous shell script in repository. build_file: "grpc-java/buildscripts/kokoro/xds-k8s.sh" -timeout_mins: 120 +timeout_mins: 180 action { define_artifacts { From ab7f867a4a2aa6211f315984035ed3730be2294d Mon Sep 17 00:00:00 2001 From: cfredri4 <50839054+cfredri4@users.noreply.github.com> Date: Mon, 8 Nov 2021 19:25:03 +0100 Subject: [PATCH 27/50] xds: Fix incorrect (old) javadoc for BootstrapperImpl (#8671) --- xds/src/main/java/io/grpc/xds/BootstrapperImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xds/src/main/java/io/grpc/xds/BootstrapperImpl.java b/xds/src/main/java/io/grpc/xds/BootstrapperImpl.java index c11dbbf7659..35ff5e55a42 100644 --- a/xds/src/main/java/io/grpc/xds/BootstrapperImpl.java +++ b/xds/src/main/java/io/grpc/xds/BootstrapperImpl.java @@ -83,7 +83,7 @@ public BootstrapperImpl() { *

  • A filesystem path defined by environment variable "GRPC_XDS_BOOTSTRAP"
  • *
  • A filesystem path defined by Java System Property "io.grpc.xds.bootstrap"
  • *
  • Environment variable value of "GRPC_XDS_BOOTSTRAP_CONFIG"
  • - *
  • Java System Property value of "io.grpc.xds.bootstrap_value"
  • + *
  • Java System Property value of "io.grpc.xds.bootstrapConfig"
  • * */ @SuppressWarnings("unchecked") From 0b0079c8a136a8fd011be5b74fef3bb906f4b4d5 Mon Sep 17 00:00:00 2001 From: yifeizhuang Date: Mon, 8 Nov 2021 15:21:59 -0800 Subject: [PATCH 28/50] xds: fix xdsClient resource not exist for invalid resource, fix xdsServerWrapper start on resource not exist (#8660) Fix bugs: 1. Invalid resource at xdsClient, the watcher should have been delivered an error instead of resource not found. 2. If the resource is properly determined to not exist, it shouldn't cause start() to fail. From A36 xDS for Servers: "XdsServer's start must not fail due to transient xDS issues, like missing xDS configuration from the xDS server." --- .../java/io/grpc/xds/ClientXdsClient.java | 10 ++-- .../java/io/grpc/xds/XdsServerWrapper.java | 4 -- .../io/grpc/xds/ClientXdsClientTestBase.java | 50 +++++++++++++------ .../XdsClientWrapperForServerSdsTestMisc.java | 16 +++--- .../io/grpc/xds/XdsServerWrapperTest.java | 22 +++++--- 5 files changed, 64 insertions(+), 38 deletions(-) diff --git a/xds/src/main/java/io/grpc/xds/ClientXdsClient.java b/xds/src/main/java/io/grpc/xds/ClientXdsClient.java index c4fedea4aa4..005da16a012 100644 --- a/xds/src/main/java/io/grpc/xds/ClientXdsClient.java +++ b/xds/src/main/java/io/grpc/xds/ClientXdsClient.java @@ -2209,11 +2209,13 @@ private void handleResourceUpdate( } retainedResources.add(edsName); } - continue; + } else if (invalidResources.contains(resourceName)) { + subscriber.onError(Status.UNAVAILABLE.withDescription(errorDetail)); + } else { + // For State of the World services, notify watchers when their watched resource is missing + // from the ADS update. + subscriber.onAbsent(); } - // For State of the World services, notify watchers when their watched resource is missing - // from the ADS update. - subscriber.onAbsent(); } } // LDS/CDS responses represents the state of the world, RDS/EDS resources not referenced in diff --git a/xds/src/main/java/io/grpc/xds/XdsServerWrapper.java b/xds/src/main/java/io/grpc/xds/XdsServerWrapper.java index ed51ccc9edf..dab3cd798f7 100644 --- a/xds/src/main/java/io/grpc/xds/XdsServerWrapper.java +++ b/xds/src/main/java/io/grpc/xds/XdsServerWrapper.java @@ -571,10 +571,6 @@ private void handleConfigNotFound(StatusException exception) { for (SslContextProviderSupplier s: toRelease) { s.close(); } - if (!initialStarted) { - initialStarted = true; - initialStartFuture.set(exception); - } if (restartTimer != null) { restartTimer.cancel(); } diff --git a/xds/src/test/java/io/grpc/xds/ClientXdsClientTestBase.java b/xds/src/test/java/io/grpc/xds/ClientXdsClientTestBase.java index 9809738c68d..4a34538954c 100644 --- a/xds/src/test/java/io/grpc/xds/ClientXdsClientTestBase.java +++ b/xds/src/test/java/io/grpc/xds/ClientXdsClientTestBase.java @@ -1563,12 +1563,16 @@ public void cdsResponseErrorHandling_badUpstreamTlsContext() { call.sendResponse(CDS, clusters, VERSION_1, "0000"); // The response NACKed with errors indicating indices of the failed resources. - call.verifyRequestNack(CDS, CDS_RESOURCE, "", "0000", NODE, ImmutableList.of( - "CDS response Cluster 'cluster.googleapis.com' validation error: " + String errorMsg = "CDS response Cluster 'cluster.googleapis.com' validation error: " + "Cluster cluster.googleapis.com: malformed UpstreamTlsContext: " + "io.grpc.xds.ClientXdsClient$ResourceInvalidException: " - + "ca_certificate_provider_instance is required in upstream-tls-context")); - verifyNoInteractions(cdsResourceWatcher); + + "ca_certificate_provider_instance is required in upstream-tls-context"; + call.verifyRequestNack(CDS, CDS_RESOURCE, "", "0000", NODE, ImmutableList.of(errorMsg)); + ArgumentCaptor captor = ArgumentCaptor.forClass(Status.class); + verify(cdsResourceWatcher).onError(captor.capture()); + Status errorStatus = captor.getValue(); + assertThat(errorStatus.getCode()).isEqualTo(Status.UNAVAILABLE.getCode()); + assertThat(errorStatus.getDescription()).isEqualTo(errorMsg); } /** @@ -1587,10 +1591,14 @@ public void cdsResponseErrorHandling_badTransportSocketName() { call.sendResponse(CDS, clusters, VERSION_1, "0000"); // The response NACKed with errors indicating indices of the failed resources. - call.verifyRequestNack(CDS, CDS_RESOURCE, "", "0000", NODE, ImmutableList.of( - "CDS response Cluster 'cluster.googleapis.com' validation error: " - + "transport-socket with name envoy.transport_sockets.bad not supported.")); - verifyNoInteractions(cdsResourceWatcher); + String errorMsg = "CDS response Cluster 'cluster.googleapis.com' validation error: " + + "transport-socket with name envoy.transport_sockets.bad not supported."; + call.verifyRequestNack(CDS, CDS_RESOURCE, "", "0000", NODE, ImmutableList.of(errorMsg)); + ArgumentCaptor captor = ArgumentCaptor.forClass(Status.class); + verify(cdsResourceWatcher).onError(captor.capture()); + Status errorStatus = captor.getValue(); + assertThat(errorStatus.getCode()).isEqualTo(Status.UNAVAILABLE.getCode()); + assertThat(errorStatus.getDescription()).isEqualTo(errorMsg); } @Test @@ -2438,10 +2446,15 @@ public void serverSideListenerResponseErrorHandling_badDownstreamTlsContext() { List listeners = ImmutableList.of(Any.pack(listener)); call.sendResponse(ResourceType.LDS, listeners, "0", "0000"); // The response NACKed with errors indicating indices of the failed resources. - call.verifyRequestNack(LDS, LISTENER_RESOURCE, "", "0000", NODE, ImmutableList.of( - "LDS response Listener \'grpc/server?xds.resource.listening_address=0.0.0.0:7000\' " - + "validation error: common-tls-context is required in downstream-tls-context")); - verifyNoInteractions(ldsResourceWatcher); + String errorMsg = "LDS response Listener \'grpc/server?xds.resource.listening_address=" + + "0.0.0.0:7000\' validation error: " + + "common-tls-context is required in downstream-tls-context"; + call.verifyRequestNack(LDS, LISTENER_RESOURCE, "", "0000", NODE, ImmutableList.of(errorMsg)); + ArgumentCaptor captor = ArgumentCaptor.forClass(Status.class); + verify(ldsResourceWatcher).onError(captor.capture()); + Status errorStatus = captor.getValue(); + assertThat(errorStatus.getCode()).isEqualTo(Status.UNAVAILABLE.getCode()); + assertThat(errorStatus.getDescription()).isEqualTo(errorMsg); } @Test @@ -2462,11 +2475,16 @@ public void serverSideListenerResponseErrorHandling_badTransportSocketName() { List listeners = ImmutableList.of(Any.pack(listener)); call.sendResponse(ResourceType.LDS, listeners, "0", "0000"); // The response NACKed with errors indicating indices of the failed resources. + String errorMsg = "LDS response Listener \'grpc/server?xds.resource.listening_address=" + + "0.0.0.0:7000\' validation error: " + + "transport-socket with name envoy.transport_sockets.bad1 not supported."; call.verifyRequestNack(LDS, LISTENER_RESOURCE, "", "0000", NODE, ImmutableList.of( - "LDS response Listener \'grpc/server?xds.resource.listening_address=0.0.0.0:7000\' " - + "validation error: " - + "transport-socket with name envoy.transport_sockets.bad1 not supported.")); - verifyNoInteractions(ldsResourceWatcher); + errorMsg)); + ArgumentCaptor captor = ArgumentCaptor.forClass(Status.class); + verify(ldsResourceWatcher).onError(captor.capture()); + Status errorStatus = captor.getValue(); + assertThat(errorStatus.getCode()).isEqualTo(Status.UNAVAILABLE.getCode()); + assertThat(errorStatus.getDescription()).isEqualTo(errorMsg); } private DiscoveryRpcCall startResourceWatcher( diff --git a/xds/src/test/java/io/grpc/xds/XdsClientWrapperForServerSdsTestMisc.java b/xds/src/test/java/io/grpc/xds/XdsClientWrapperForServerSdsTestMisc.java index 1871cb79770..a39a5495c09 100644 --- a/xds/src/test/java/io/grpc/xds/XdsClientWrapperForServerSdsTestMisc.java +++ b/xds/src/test/java/io/grpc/xds/XdsClientWrapperForServerSdsTestMisc.java @@ -63,15 +63,15 @@ import io.netty.handler.codec.http2.Http2ConnectionDecoder; import io.netty.handler.codec.http2.Http2ConnectionEncoder; import io.netty.handler.codec.http2.Http2Settings; -import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.util.Arrays; import java.util.Collections; -import java.util.concurrent.ExecutionException; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -190,8 +190,8 @@ public void run() { try { start.get(5, TimeUnit.SECONDS); fail("Start should throw exception"); - } catch (ExecutionException ex) { - assertThat(ex.getCause()).isInstanceOf(IOException.class); + } catch (TimeoutException ex) { + assertThat(start.isDone()).isFalse(); } assertThat(selectorManager.getSelectorToUpdateSelector()).isSameInstanceAs(NO_FILTER_CHAIN); } @@ -214,8 +214,8 @@ public void run() { try { start.get(5, TimeUnit.SECONDS); fail("Start should throw exception"); - } catch (ExecutionException ex) { - assertThat(ex.getCause()).isInstanceOf(IOException.class); + } catch (TimeoutException ex) { + assertThat(start.isDone()).isFalse(); } assertThat(selectorManager.getSelectorToUpdateSelector()).isSameInstanceAs(NO_FILTER_CHAIN); } @@ -238,8 +238,8 @@ public void run() { try { start.get(5, TimeUnit.SECONDS); fail("Start should throw exception"); - } catch (ExecutionException ex) { - assertThat(ex.getCause()).isInstanceOf(IOException.class); + } catch (TimeoutException ex) { + assertThat(start.isDone()).isFalse(); } assertThat(selectorManager.getSelectorToUpdateSelector()).isSameInstanceAs(NO_FILTER_CHAIN); } diff --git a/xds/src/test/java/io/grpc/xds/XdsServerWrapperTest.java b/xds/src/test/java/io/grpc/xds/XdsServerWrapperTest.java index e68d0f5175c..1bd102db42d 100644 --- a/xds/src/test/java/io/grpc/xds/XdsServerWrapperTest.java +++ b/xds/src/test/java/io/grpc/xds/XdsServerWrapperTest.java @@ -74,6 +74,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicReference; import org.junit.Before; import org.junit.Rule; @@ -261,9 +262,10 @@ public void run() { xdsClient.ldsWatcher.onResourceDoesNotExist(ldsResource); try { start.get(5000, TimeUnit.MILLISECONDS); - fail("Start should throw exception"); - } catch (ExecutionException ex) { - assertThat(ex.getCause()).isInstanceOf(IOException.class); + fail("server should not start() successfully."); + } catch (TimeoutException ex) { + // expect to block here. + assertThat(start.isDone()).isFalse(); } verify(mockBuilder, times(1)).build(); verify(mockServer, never()).start(); @@ -602,9 +604,10 @@ public void run() { xdsClient.ldsWatcher.onResourceDoesNotExist(ldsResource); try { start.get(5000, TimeUnit.MILLISECONDS); - fail("Start should throw exception"); - } catch (ExecutionException ex) { - assertThat(ex.getCause()).isInstanceOf(IOException.class); + fail("server should not start()"); + } catch (TimeoutException ex) { + // expect to block here. + assertThat(start.isDone()).isFalse(); } verify(listener, times(1)).onNotServing(any(StatusException.class)); verify(mockBuilder, times(1)).build(); @@ -627,6 +630,13 @@ public void run() { assertThat(sslSupplier0.isShutdown()).isTrue(); xdsClient.deliverRdsUpdate("rds", Collections.singletonList(createVirtualHost("virtual-host-1"))); + try { + start.get(5000, TimeUnit.MILLISECONDS); + fail("Start should throw exception"); + } catch (ExecutionException ex) { + assertThat(ex.getCause()).isInstanceOf(IOException.class); + assertThat(ex.getCause().getMessage()).isEqualTo("error!"); + } RdsResourceWatcher saveRdsWatcher = xdsClient.rdsWatchers.get("rds"); assertThat(executor.forwardNanos(RETRY_DELAY_NANOS)).isEqualTo(1); verify(mockBuilder, times(1)).build(); From b6eafbe695939436f87bd0c3bb089244c3986602 Mon Sep 17 00:00:00 2001 From: Eric Anderson Date: Tue, 9 Nov 2021 08:41:11 -0800 Subject: [PATCH 29/50] netty: Add system property to disable Connection header check A user has a proxy that is sending "Connection: close", which is against the HTTP/2 spec, but will take time to fix. Fixes #8674 --- netty/src/main/java/io/grpc/netty/NettyServerHandler.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/netty/src/main/java/io/grpc/netty/NettyServerHandler.java b/netty/src/main/java/io/grpc/netty/NettyServerHandler.java index c286c17f640..f552b937a05 100644 --- a/netty/src/main/java/io/grpc/netty/NettyServerHandler.java +++ b/netty/src/main/java/io/grpc/netty/NettyServerHandler.java @@ -109,6 +109,9 @@ class NettyServerHandler extends AbstractNettyHandler { @VisibleForTesting static final long GRACEFUL_SHUTDOWN_PING = 0x97ACEF001L; private static final long GRACEFUL_SHUTDOWN_PING_TIMEOUT_NANOS = TimeUnit.SECONDS.toNanos(10); + /** Temporary workaround for #8674. Fine to delete after v1.45 release, and maybe earlier. */ + private static final boolean DISABLE_CONNECTION_HEADER_CHECK = Boolean.parseBoolean( + System.getProperty("io.grpc.netty.disableConnectionHeaderCheck", "false")); private final Http2Connection.PropertyKey streamKey; private final ServerTransportListener transportListener; @@ -380,7 +383,7 @@ private void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers try { // Connection-specific header fields makes a request malformed. Ideally this would be handled // by Netty. RFC 7540 section 8.1.2.2 - if (headers.contains(CONNECTION)) { + if (!DISABLE_CONNECTION_HEADER_CHECK && headers.contains(CONNECTION)) { resetStream(ctx, streamId, Http2Error.PROTOCOL_ERROR.code(), ctx.newPromise()); return; } From 6518d7bd6dc496c89ff320e06c73424f26af364a Mon Sep 17 00:00:00 2001 From: Kevin Wooten Date: Tue, 9 Nov 2021 11:36:04 -0700 Subject: [PATCH 30/50] Copy macOS x86 artifacts to aarch during upload (#8680) --- buildscripts/kokoro/upload_artifacts.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/buildscripts/kokoro/upload_artifacts.sh b/buildscripts/kokoro/upload_artifacts.sh index 8d7f2f5b365..20e16c38a2f 100644 --- a/buildscripts/kokoro/upload_artifacts.sh +++ b/buildscripts/kokoro/upload_artifacts.sh @@ -33,6 +33,8 @@ LOCAL_OTHER_ARTIFACTS="$KOKORO_GFILE_DIR"/github/grpc-java/artifacts/ # from macos job: [[ "$(find "$LOCAL_MVN_ARTIFACTS" -type f -iname 'protoc-gen-grpc-java-*-osx-x86_64.exe' | wc -l)" != '0' ]] +# copy all x86 artifacts to aarch until native artifacts are built +find "$LOCAL_MVN_ARTIFACTS" -type f -iname 'protoc-gen-grpc-java-*-osx-x86_64.exe*' -exec bash -c 'cp "${0}" "${0/x86/aarch}"' {} \; # from windows job: [[ "$(find "$LOCAL_MVN_ARTIFACTS" -type f -iname 'protoc-gen-grpc-java-*-windows-x86_64.exe' | wc -l)" != '0' ]] From 389b865b9ba88cc16354b84023cf21a1a8e2e632 Mon Sep 17 00:00:00 2001 From: ZHANG Dapeng Date: Tue, 9 Nov 2021 16:37:54 -0800 Subject: [PATCH 31/50] xds: populate LRS ServerInfo to CdsUpdate (#8676) Replace `String lrsServerName` with `ServerInfo lrsServerInfo` in `CdsUpdate`. See http://go/grpc-xds-federation#heading=h.gh3gjftay27x for details. This PR is only refactoring. Federation support is not implemented until the TODO [here](https://github.com/grpc/grpc-java/blob/a5c526c12f699af9a846d9faabc18038ef431675/xds/src/main/java/io/grpc/xds/ClientXdsClient.java#L2280) is addressed. Resolves #8628 --- .../java/io/grpc/xds/CdsLoadBalancer2.java | 4 +- .../java/io/grpc/xds/ClientXdsClient.java | 27 +++++---- .../io/grpc/xds/ClusterImplLoadBalancer.java | 27 ++++----- .../xds/ClusterImplLoadBalancerProvider.java | 11 ++-- .../grpc/xds/ClusterResolverLoadBalancer.java | 31 ++++++----- .../ClusterResolverLoadBalancerProvider.java | 23 ++++---- xds/src/main/java/io/grpc/xds/XdsClient.java | 24 ++++---- .../io/grpc/xds/CdsLoadBalancer2Test.java | 55 ++++++++++--------- .../io/grpc/xds/ClientXdsClientDataTest.java | 14 +++-- .../io/grpc/xds/ClientXdsClientTestBase.java | 33 ++++++----- .../grpc/xds/ClusterImplLoadBalancerTest.java | 37 +++++++------ .../xds/ClusterResolverLoadBalancerTest.java | 25 +++++---- 12 files changed, 167 insertions(+), 144 deletions(-) diff --git a/xds/src/main/java/io/grpc/xds/CdsLoadBalancer2.java b/xds/src/main/java/io/grpc/xds/CdsLoadBalancer2.java index 4af187bf1dd..396292a2392 100644 --- a/xds/src/main/java/io/grpc/xds/CdsLoadBalancer2.java +++ b/xds/src/main/java/io/grpc/xds/CdsLoadBalancer2.java @@ -157,12 +157,12 @@ private void handleClusterDiscovered() { if (clusterState.result.clusterType() == ClusterType.EDS) { instance = DiscoveryMechanism.forEds( clusterState.name, clusterState.result.edsServiceName(), - clusterState.result.lrsServerName(), clusterState.result.maxConcurrentRequests(), + clusterState.result.lrsServerInfo(), clusterState.result.maxConcurrentRequests(), clusterState.result.upstreamTlsContext()); } else { // logical DNS instance = DiscoveryMechanism.forLogicalDns( clusterState.name, clusterState.result.dnsHostName(), - clusterState.result.lrsServerName(), clusterState.result.maxConcurrentRequests(), + clusterState.result.lrsServerInfo(), clusterState.result.maxConcurrentRequests(), clusterState.result.upstreamTlsContext()); } instances.add(instance); diff --git a/xds/src/main/java/io/grpc/xds/ClientXdsClient.java b/xds/src/main/java/io/grpc/xds/ClientXdsClient.java index 005da16a012..c8172a87d18 100644 --- a/xds/src/main/java/io/grpc/xds/ClientXdsClient.java +++ b/xds/src/main/java/io/grpc/xds/ClientXdsClient.java @@ -1471,7 +1471,7 @@ public void handleCdsResponse( if (getBootstrapInfo() != null && getBootstrapInfo().certProviders() != null) { certProviderInstances = getBootstrapInfo().certProviders().keySet(); } - cdsUpdate = parseCluster(cluster, retainedEdsResources, certProviderInstances); + cdsUpdate = parseCluster(cluster, retainedEdsResources, certProviderInstances, serverInfo); } catch (ResourceInvalidException e) { errors.add( "CDS response Cluster '" + clusterName + "' validation error: " + e.getMessage()); @@ -1490,13 +1490,13 @@ public void handleCdsResponse( @VisibleForTesting static CdsUpdate parseCluster(Cluster cluster, Set retainedEdsResources, - Set certProviderInstances) + Set certProviderInstances, ServerInfo serverInfo) throws ResourceInvalidException { StructOrError structOrError; switch (cluster.getClusterDiscoveryTypeCase()) { case TYPE: structOrError = parseNonAggregateCluster(cluster, retainedEdsResources, - certProviderInstances); + certProviderInstances, serverInfo); break; case CLUSTER_TYPE: structOrError = parseAggregateCluster(cluster); @@ -1559,9 +1559,10 @@ private static StructOrError parseAggregateCluster(Cluster cl } private static StructOrError parseNonAggregateCluster( - Cluster cluster, Set edsResources, Set certProviderInstances) { + Cluster cluster, Set edsResources, Set certProviderInstances, + ServerInfo serverInfo) { String clusterName = cluster.getName(); - String lrsServerName = null; + ServerInfo lrsServerInfo = null; Long maxConcurrentRequests = null; UpstreamTlsContext upstreamTlsContext = null; if (cluster.hasLrsServer()) { @@ -1569,7 +1570,7 @@ private static StructOrError parseNonAggregateCluster( return StructOrError.fromError( "Cluster " + clusterName + ": only support LRS for the same management server"); } - lrsServerName = ""; + lrsServerInfo = serverInfo; } if (cluster.hasCircuitBreakers()) { List thresholds = cluster.getCircuitBreakers().getThresholdsList(); @@ -1621,7 +1622,7 @@ private static StructOrError parseNonAggregateCluster( edsResources.add(clusterName); } return StructOrError.fromStruct(CdsUpdate.forEds( - clusterName, edsServiceName, lrsServerName, maxConcurrentRequests, upstreamTlsContext)); + clusterName, edsServiceName, lrsServerInfo, maxConcurrentRequests, upstreamTlsContext)); } else if (type.equals(DiscoveryType.LOGICAL_DNS)) { if (!cluster.hasLoadAssignment()) { return StructOrError.fromError( @@ -1656,7 +1657,7 @@ private static StructOrError parseNonAggregateCluster( String dnsHostName = String.format("%s:%d", socketAddress.getAddress(), socketAddress.getPortValue()); return StructOrError.fromStruct(CdsUpdate.forLogicalDns( - clusterName, dnsHostName, lrsServerName, maxConcurrentRequests, upstreamTlsContext)); + clusterName, dnsHostName, lrsServerInfo, maxConcurrentRequests, upstreamTlsContext)); } return StructOrError.fromError( "Cluster " + clusterName + ": unsupported built-in discovery type: " + type); @@ -2094,15 +2095,14 @@ public void run() { @Override ClusterDropStats addClusterDropStats( - String clusterName, @Nullable String edsServiceName) { + final ServerInfo serverInfo, String clusterName, @Nullable String edsServiceName) { ClusterDropStats dropCounter = loadStatsManager.getClusterDropStats(clusterName, edsServiceName); syncContext.execute(new Runnable() { @Override public void run() { if (!reportingLoad) { - // TODO(https://github.com/grpc/grpc-java/issues/8628): consume ServerInfo arg. - serverLrsClientMap.values().iterator().next().startLoadReporting(); + serverLrsClientMap.get(serverInfo).startLoadReporting(); reportingLoad = true; } } @@ -2112,7 +2112,7 @@ public void run() { @Override ClusterLocalityStats addClusterLocalityStats( - String clusterName, @Nullable String edsServiceName, + final ServerInfo serverInfo, String clusterName, @Nullable String edsServiceName, Locality locality) { ClusterLocalityStats loadCounter = loadStatsManager.getClusterLocalityStats(clusterName, edsServiceName, locality); @@ -2120,8 +2120,7 @@ ClusterLocalityStats addClusterLocalityStats( @Override public void run() { if (!reportingLoad) { - // TODO(https://github.com/grpc/grpc-java/issues/8628): consume ServerInfo arg. - serverLrsClientMap.values().iterator().next().startLoadReporting(); + serverLrsClientMap.get(serverInfo).startLoadReporting(); reportingLoad = true; } } diff --git a/xds/src/main/java/io/grpc/xds/ClusterImplLoadBalancer.java b/xds/src/main/java/io/grpc/xds/ClusterImplLoadBalancer.java index 330c4e2f7a5..60e6c696daf 100644 --- a/xds/src/main/java/io/grpc/xds/ClusterImplLoadBalancer.java +++ b/xds/src/main/java/io/grpc/xds/ClusterImplLoadBalancer.java @@ -35,6 +35,7 @@ import io.grpc.internal.ObjectPool; import io.grpc.util.ForwardingLoadBalancerHelper; import io.grpc.util.ForwardingSubchannel; +import io.grpc.xds.Bootstrapper.ServerInfo; import io.grpc.xds.ClusterImplLoadBalancerProvider.ClusterImplConfig; import io.grpc.xds.Endpoints.DropOverload; import io.grpc.xds.EnvoyServerProtoData.UpstreamTlsContext; @@ -117,17 +118,12 @@ public void handleResolvedAddresses(ResolvedAddresses resolvedAddresses) { cluster = config.cluster; edsServiceName = config.edsServiceName; childLbHelper = new ClusterImplLbHelper( - callCounterProvider.getOrCreate(config.cluster, config.edsServiceName)); + callCounterProvider.getOrCreate(config.cluster, config.edsServiceName), + config.lrsServerInfo); childLb = config.childPolicy.getProvider().newLoadBalancer(childLbHelper); // Assume load report server does not change throughout cluster lifetime. - if (config.lrsServerName != null) { - if (config.lrsServerName.isEmpty()) { - dropStats = xdsClient.addClusterDropStats(cluster, edsServiceName); - } else { - logger.log(XdsLogLevel.WARNING, "Cluster {0} config error: can only report load " - + "to the same management server. Config lrsServerName {1} should be empty. ", - cluster, config.lrsServerName); - } + if (config.lrsServerInfo != null) { + dropStats = xdsClient.addClusterDropStats(config.lrsServerInfo, cluster, edsServiceName); } } childLbHelper.updateDropPolicies(config.dropCategories); @@ -183,9 +179,12 @@ private final class ClusterImplLbHelper extends ForwardingLoadBalancerHelper { private long maxConcurrentRequests = DEFAULT_PER_CLUSTER_MAX_CONCURRENT_REQUESTS; @Nullable private SslContextProviderSupplier sslContextProviderSupplier; + @Nullable + private final ServerInfo lrsServerInfo; - private ClusterImplLbHelper(AtomicLong inFlights) { + private ClusterImplLbHelper(AtomicLong inFlights, @Nullable ServerInfo lrsServerInfo) { this.inFlights = checkNotNull(inFlights, "inFlights"); + this.lrsServerInfo = lrsServerInfo; } @Override @@ -218,8 +217,8 @@ public Subchannel createSubchannel(CreateSubchannelArgs args) { if (locality == null) { locality = Locality.create("", "", ""); } - final ClusterLocalityStats localityStats = xdsClient.addClusterLocalityStats( - cluster, edsServiceName, locality); + final ClusterLocalityStats localityStats = lrsServerInfo == null ? null + : xdsClient.addClusterLocalityStats(lrsServerInfo, cluster, edsServiceName, locality); Attributes attrs = args.getAttributes().toBuilder().set( ATTR_CLUSTER_LOCALITY_STATS, localityStats).build(); args = args.toBuilder().setAddresses(addresses).setAttributes(attrs).build(); @@ -228,7 +227,9 @@ public Subchannel createSubchannel(CreateSubchannelArgs args) { return new ForwardingSubchannel() { @Override public void shutdown() { - localityStats.release(); + if (localityStats != null) { + localityStats.release(); + } delegate().shutdown(); } diff --git a/xds/src/main/java/io/grpc/xds/ClusterImplLoadBalancerProvider.java b/xds/src/main/java/io/grpc/xds/ClusterImplLoadBalancerProvider.java index 939734fe4f0..11e649474b7 100644 --- a/xds/src/main/java/io/grpc/xds/ClusterImplLoadBalancerProvider.java +++ b/xds/src/main/java/io/grpc/xds/ClusterImplLoadBalancerProvider.java @@ -26,6 +26,7 @@ import io.grpc.LoadBalancerRegistry; import io.grpc.NameResolver.ConfigOrError; import io.grpc.internal.ServiceConfigUtil.PolicySelection; +import io.grpc.xds.Bootstrapper.ServerInfo; import io.grpc.xds.Endpoints.DropOverload; import io.grpc.xds.EnvoyServerProtoData.UpstreamTlsContext; import java.util.ArrayList; @@ -73,9 +74,9 @@ static final class ClusterImplConfig { // Resource name used in discovering endpoints via EDS. Only valid for EDS clusters. @Nullable final String edsServiceName; - // Load report server name. Null if load reporting is disabled. + // Load report server info. Null if load reporting is disabled. @Nullable - final String lrsServerName; + final ServerInfo lrsServerInfo; // Cluster-level max concurrent request threshold. Null if not specified. @Nullable final Long maxConcurrentRequests; @@ -88,12 +89,12 @@ static final class ClusterImplConfig { final PolicySelection childPolicy; ClusterImplConfig(String cluster, @Nullable String edsServiceName, - @Nullable String lrsServerName, @Nullable Long maxConcurrentRequests, + @Nullable ServerInfo lrsServerInfo, @Nullable Long maxConcurrentRequests, List dropCategories, PolicySelection childPolicy, @Nullable UpstreamTlsContext tlsContext) { this.cluster = checkNotNull(cluster, "cluster"); this.edsServiceName = edsServiceName; - this.lrsServerName = lrsServerName; + this.lrsServerInfo = lrsServerInfo; this.maxConcurrentRequests = maxConcurrentRequests; this.tlsContext = tlsContext; this.dropCategories = Collections.unmodifiableList( @@ -106,7 +107,7 @@ public String toString() { return MoreObjects.toStringHelper(this) .add("cluster", cluster) .add("edsServiceName", edsServiceName) - .add("lrsServerName", lrsServerName) + .add("lrsServerInfo", lrsServerInfo) .add("maxConcurrentRequests", maxConcurrentRequests) // Exclude tlsContext as its string representation is cumbersome. .add("dropCategories", dropCategories) diff --git a/xds/src/main/java/io/grpc/xds/ClusterResolverLoadBalancer.java b/xds/src/main/java/io/grpc/xds/ClusterResolverLoadBalancer.java index 9ba7541e314..f4fcabcb21f 100644 --- a/xds/src/main/java/io/grpc/xds/ClusterResolverLoadBalancer.java +++ b/xds/src/main/java/io/grpc/xds/ClusterResolverLoadBalancer.java @@ -39,6 +39,7 @@ import io.grpc.internal.ServiceConfigUtil.PolicySelection; import io.grpc.util.ForwardingLoadBalancerHelper; import io.grpc.util.GracefulSwitchLoadBalancer; +import io.grpc.xds.Bootstrapper.ServerInfo; import io.grpc.xds.ClusterImplLoadBalancerProvider.ClusterImplConfig; import io.grpc.xds.ClusterResolverLoadBalancerProvider.ClusterResolverConfig; import io.grpc.xds.ClusterResolverLoadBalancerProvider.ClusterResolverConfig.DiscoveryMechanism; @@ -174,10 +175,10 @@ public void handleResolvedAddresses(ResolvedAddresses resolvedAddresses) { ClusterState state; if (instance.type == DiscoveryMechanism.Type.EDS) { state = new EdsClusterState(instance.cluster, instance.edsServiceName, - instance.lrsServerName, instance.maxConcurrentRequests, instance.tlsContext); + instance.lrsServerInfo, instance.maxConcurrentRequests, instance.tlsContext); } else { // logical DNS state = new LogicalDnsClusterState(instance.cluster, instance.dnsHostName, - instance.lrsServerName, instance.maxConcurrentRequests, instance.tlsContext); + instance.lrsServerInfo, instance.maxConcurrentRequests, instance.tlsContext); } clusterStates.put(instance.cluster, state); state.start(); @@ -305,7 +306,7 @@ private abstract class ClusterState { // Name of the cluster to be resolved. protected final String name; @Nullable - protected final String lrsServerName; + protected final ServerInfo lrsServerInfo; @Nullable protected final Long maxConcurrentRequests; @Nullable @@ -319,10 +320,10 @@ private abstract class ClusterState { protected ClusterResolutionResult result; protected boolean shutdown; - private ClusterState(String name, @Nullable String lrsServerName, + private ClusterState(String name, @Nullable ServerInfo lrsServerInfo, @Nullable Long maxConcurrentRequests, @Nullable UpstreamTlsContext tlsContext) { this.name = name; - this.lrsServerName = lrsServerName; + this.lrsServerInfo = lrsServerInfo; this.maxConcurrentRequests = maxConcurrentRequests; this.tlsContext = tlsContext; } @@ -339,9 +340,9 @@ private final class EdsClusterState extends ClusterState implements EdsResourceW private final String edsServiceName; private EdsClusterState(String name, @Nullable String edsServiceName, - @Nullable String lrsServerName, @Nullable Long maxConcurrentRequests, + @Nullable ServerInfo lrsServerInfo, @Nullable Long maxConcurrentRequests, @Nullable UpstreamTlsContext tlsContext) { - super(name, lrsServerName, maxConcurrentRequests, tlsContext); + super(name, lrsServerInfo, maxConcurrentRequests, tlsContext); this.edsServiceName = edsServiceName; } @@ -423,7 +424,7 @@ public void run() { Collections.sort(priorities); Map priorityChildConfigs = generateEdsBasedPriorityChildConfigs( - name, edsServiceName, lrsServerName, maxConcurrentRequests, tlsContext, + name, edsServiceName, lrsServerInfo, maxConcurrentRequests, tlsContext, endpointLbPolicy, lbRegistry, prioritizedLocalityWeights, dropOverloads); status = Status.OK; resolved = true; @@ -479,9 +480,9 @@ private final class LogicalDnsClusterState extends ClusterState { private ScheduledHandle scheduledRefresh; private LogicalDnsClusterState(String name, String dnsHostName, - @Nullable String lrsServerName, @Nullable Long maxConcurrentRequests, + @Nullable ServerInfo lrsServerInfo, @Nullable Long maxConcurrentRequests, @Nullable UpstreamTlsContext tlsContext) { - super(name, lrsServerName, maxConcurrentRequests, tlsContext); + super(name, lrsServerInfo, maxConcurrentRequests, tlsContext); this.dnsHostName = checkNotNull(dnsHostName, "dnsHostName"); nameResolverFactory = checkNotNull(helper.getNameResolverRegistry().asFactory(), "nameResolverFactory"); @@ -568,7 +569,7 @@ public void run() { addresses.add(eag); } PriorityChildConfig priorityChildConfig = generateDnsBasedPriorityChildConfig( - name, lrsServerName, maxConcurrentRequests, tlsContext, lbRegistry, + name, lrsServerInfo, maxConcurrentRequests, tlsContext, lbRegistry, Collections.emptyList()); status = Status.OK; resolved = true; @@ -651,14 +652,14 @@ private static class ClusterResolutionResult { *

    priority LB -> cluster_impl LB (single hardcoded priority) -> pick_first */ private static PriorityChildConfig generateDnsBasedPriorityChildConfig( - String cluster, @Nullable String lrsServerName, @Nullable Long maxConcurrentRequests, + String cluster, @Nullable ServerInfo lrsServerInfo, @Nullable Long maxConcurrentRequests, @Nullable UpstreamTlsContext tlsContext, LoadBalancerRegistry lbRegistry, List dropOverloads) { // Override endpoint-level LB policy with pick_first for logical DNS cluster. PolicySelection endpointLbPolicy = new PolicySelection(lbRegistry.getProvider("pick_first"), null); ClusterImplConfig clusterImplConfig = - new ClusterImplConfig(cluster, null, lrsServerName, maxConcurrentRequests, + new ClusterImplConfig(cluster, null, lrsServerInfo, maxConcurrentRequests, dropOverloads, endpointLbPolicy, tlsContext); LoadBalancerProvider clusterImplLbProvider = lbRegistry.getProvider(XdsLbPolicies.CLUSTER_IMPL_POLICY_NAME); @@ -674,7 +675,7 @@ private static PriorityChildConfig generateDnsBasedPriorityChildConfig( * -> round_robin (one per locality)) / ring_hash */ private static Map generateEdsBasedPriorityChildConfigs( - String cluster, @Nullable String edsServiceName, @Nullable String lrsServerName, + String cluster, @Nullable String edsServiceName, @Nullable ServerInfo lrsServerInfo, @Nullable Long maxConcurrentRequests, @Nullable UpstreamTlsContext tlsContext, PolicySelection endpointLbPolicy, LoadBalancerRegistry lbRegistry, Map> prioritizedLocalityWeights, @@ -704,7 +705,7 @@ private static Map generateEdsBasedPriorityChildCon leafPolicy = new PolicySelection(weightedTargetLbProvider, weightedTargetConfig); } ClusterImplConfig clusterImplConfig = - new ClusterImplConfig(cluster, edsServiceName, lrsServerName, maxConcurrentRequests, + new ClusterImplConfig(cluster, edsServiceName, lrsServerInfo, maxConcurrentRequests, dropOverloads, leafPolicy, tlsContext); LoadBalancerProvider clusterImplLbProvider = lbRegistry.getProvider(XdsLbPolicies.CLUSTER_IMPL_POLICY_NAME); diff --git a/xds/src/main/java/io/grpc/xds/ClusterResolverLoadBalancerProvider.java b/xds/src/main/java/io/grpc/xds/ClusterResolverLoadBalancerProvider.java index 33b150e667b..551c2c296fc 100644 --- a/xds/src/main/java/io/grpc/xds/ClusterResolverLoadBalancerProvider.java +++ b/xds/src/main/java/io/grpc/xds/ClusterResolverLoadBalancerProvider.java @@ -25,6 +25,7 @@ import io.grpc.LoadBalancerProvider; import io.grpc.NameResolver.ConfigOrError; import io.grpc.internal.ServiceConfigUtil.PolicySelection; +import io.grpc.xds.Bootstrapper.ServerInfo; import io.grpc.xds.EnvoyServerProtoData.UpstreamTlsContext; import java.util.List; import java.util.Map; @@ -107,9 +108,9 @@ static final class DiscoveryMechanism { final String cluster; // Type of the cluster. final Type type; - // Load reporting server name. Null if not enabled. + // Load reporting server info. Null if not enabled. @Nullable - final String lrsServerName; + final ServerInfo lrsServerInfo; // Cluster-level max concurrent request threshold. Null if not specified. @Nullable final Long maxConcurrentRequests; @@ -129,34 +130,34 @@ enum Type { } private DiscoveryMechanism(String cluster, Type type, @Nullable String edsServiceName, - @Nullable String dnsHostName, @Nullable String lrsServerName, + @Nullable String dnsHostName, @Nullable ServerInfo lrsServerInfo, @Nullable Long maxConcurrentRequests, @Nullable UpstreamTlsContext tlsContext) { this.cluster = checkNotNull(cluster, "cluster"); this.type = checkNotNull(type, "type"); this.edsServiceName = edsServiceName; this.dnsHostName = dnsHostName; - this.lrsServerName = lrsServerName; + this.lrsServerInfo = lrsServerInfo; this.maxConcurrentRequests = maxConcurrentRequests; this.tlsContext = tlsContext; } static DiscoveryMechanism forEds(String cluster, @Nullable String edsServiceName, - @Nullable String lrsServerName, @Nullable Long maxConcurrentRequests, + @Nullable ServerInfo lrsServerInfo, @Nullable Long maxConcurrentRequests, @Nullable UpstreamTlsContext tlsContext) { - return new DiscoveryMechanism(cluster, Type.EDS, edsServiceName, null, lrsServerName, + return new DiscoveryMechanism(cluster, Type.EDS, edsServiceName, null, lrsServerInfo, maxConcurrentRequests, tlsContext); } static DiscoveryMechanism forLogicalDns(String cluster, String dnsHostName, - @Nullable String lrsServerName, @Nullable Long maxConcurrentRequests, + @Nullable ServerInfo lrsServerInfo, @Nullable Long maxConcurrentRequests, @Nullable UpstreamTlsContext tlsContext) { return new DiscoveryMechanism(cluster, Type.LOGICAL_DNS, null, dnsHostName, - lrsServerName, maxConcurrentRequests, tlsContext); + lrsServerInfo, maxConcurrentRequests, tlsContext); } @Override public int hashCode() { - return Objects.hash(cluster, type, lrsServerName, maxConcurrentRequests, tlsContext, + return Objects.hash(cluster, type, lrsServerInfo, maxConcurrentRequests, tlsContext, edsServiceName, dnsHostName); } @@ -173,7 +174,7 @@ public boolean equals(Object o) { && type == that.type && Objects.equals(edsServiceName, that.edsServiceName) && Objects.equals(dnsHostName, that.dnsHostName) - && Objects.equals(lrsServerName, that.lrsServerName) + && Objects.equals(lrsServerInfo, that.lrsServerInfo) && Objects.equals(maxConcurrentRequests, that.maxConcurrentRequests) && Objects.equals(tlsContext, that.tlsContext); } @@ -186,7 +187,7 @@ public String toString() { .add("type", type) .add("edsServiceName", edsServiceName) .add("dnsHostName", dnsHostName) - .add("lrsServerName", lrsServerName) + .add("lrsServerInfo", lrsServerInfo) // Exclude tlsContext as its string representation is cumbersome. .add("maxConcurrentRequests", maxConcurrentRequests); return toStringHelper.toString(); diff --git a/xds/src/main/java/io/grpc/xds/XdsClient.java b/xds/src/main/java/io/grpc/xds/XdsClient.java index 1daa257e54e..9a6e28b3f5e 100644 --- a/xds/src/main/java/io/grpc/xds/XdsClient.java +++ b/xds/src/main/java/io/grpc/xds/XdsClient.java @@ -130,10 +130,10 @@ abstract static class CdsUpdate implements ResourceUpdate { @Nullable abstract String dnsHostName(); - // Load report server name for reporting loads via LRS. + // Load report server info for reporting loads via LRS. // Only valid for EDS or LOGICAL_DNS cluster. @Nullable - abstract String lrsServerName(); + abstract ServerInfo lrsServerInfo(); // Max number of concurrent requests can be sent to this cluster. // Only valid for EDS or LOGICAL_DNS cluster. @@ -161,7 +161,7 @@ static Builder forAggregate(String clusterName, List prioritizedClusterN } static Builder forEds(String clusterName, @Nullable String edsServiceName, - @Nullable String lrsServerName, @Nullable Long maxConcurrentRequests, + @Nullable ServerInfo lrsServerInfo, @Nullable Long maxConcurrentRequests, @Nullable UpstreamTlsContext upstreamTlsContext) { return new AutoValue_XdsClient_CdsUpdate.Builder() .clusterName(clusterName) @@ -169,13 +169,13 @@ static Builder forEds(String clusterName, @Nullable String edsServiceName, .minRingSize(0) .maxRingSize(0) .edsServiceName(edsServiceName) - .lrsServerName(lrsServerName) + .lrsServerInfo(lrsServerInfo) .maxConcurrentRequests(maxConcurrentRequests) .upstreamTlsContext(upstreamTlsContext); } static Builder forLogicalDns(String clusterName, String dnsHostName, - @Nullable String lrsServerName, @Nullable Long maxConcurrentRequests, + @Nullable ServerInfo lrsServerInfo, @Nullable Long maxConcurrentRequests, @Nullable UpstreamTlsContext upstreamTlsContext) { return new AutoValue_XdsClient_CdsUpdate.Builder() .clusterName(clusterName) @@ -183,7 +183,7 @@ static Builder forLogicalDns(String clusterName, String dnsHostName, .minRingSize(0) .maxRingSize(0) .dnsHostName(dnsHostName) - .lrsServerName(lrsServerName) + .lrsServerInfo(lrsServerInfo) .maxConcurrentRequests(maxConcurrentRequests) .upstreamTlsContext(upstreamTlsContext); } @@ -207,7 +207,7 @@ public final String toString() { .add("maxRingSize", maxRingSize()) .add("edsServiceName", edsServiceName()) .add("dnsHostName", dnsHostName()) - .add("lrsServerName", lrsServerName()) + .add("lrsServerInfo", lrsServerInfo()) .add("maxConcurrentRequests", maxConcurrentRequests()) // Exclude upstreamTlsContext as its string representation is cumbersome. .add("prioritizedClusterNames", prioritizedClusterNames()) @@ -246,7 +246,7 @@ Builder ringHashLbPolicy(long minRingSize, long maxRingSize) { protected abstract Builder dnsHostName(String dnsHostName); // Private, use one of the static factory methods instead. - protected abstract Builder lrsServerName(String lrsServerName); + protected abstract Builder lrsServerInfo(ServerInfo lrsServerInfo); // Private, use one of the static factory methods instead. protected abstract Builder maxConcurrentRequests(Long maxConcurrentRequests); @@ -569,8 +569,8 @@ void cancelEdsResourceWatch(String resourceName, EdsResourceWatcher watcher) { * use {@link ClusterDropStats#release} to release its hard reference when it is safe to * stop reporting dropped RPCs for the specified cluster in the future. */ - // TODO(https://github.com/grpc/grpc-java/issues/8628): add ServerInfo arg - ClusterDropStats addClusterDropStats(String clusterName, @Nullable String edsServiceName) { + ClusterDropStats addClusterDropStats( + ServerInfo serverInfo, String clusterName, @Nullable String edsServiceName) { throw new UnsupportedOperationException(); } @@ -582,9 +582,9 @@ ClusterDropStats addClusterDropStats(String clusterName, @Nullable String edsSer * reference when it is safe to stop reporting RPC loads for the specified locality in the * future. */ - // TODO(https://github.com/grpc/grpc-java/issues/8628): add ServerInfo arg ClusterLocalityStats addClusterLocalityStats( - String clusterName, @Nullable String edsServiceName, Locality locality) { + ServerInfo serverInfo, String clusterName, @Nullable String edsServiceName, + Locality locality) { throw new UnsupportedOperationException(); } diff --git a/xds/src/test/java/io/grpc/xds/CdsLoadBalancer2Test.java b/xds/src/test/java/io/grpc/xds/CdsLoadBalancer2Test.java index 24586c70e91..388fc6a8b20 100644 --- a/xds/src/test/java/io/grpc/xds/CdsLoadBalancer2Test.java +++ b/xds/src/test/java/io/grpc/xds/CdsLoadBalancer2Test.java @@ -29,6 +29,7 @@ import io.grpc.Attributes; import io.grpc.ConnectivityState; import io.grpc.EquivalentAddressGroup; +import io.grpc.InsecureChannelCredentials; import io.grpc.LoadBalancer; import io.grpc.LoadBalancer.Helper; import io.grpc.LoadBalancer.PickResult; @@ -42,6 +43,7 @@ import io.grpc.Status.Code; import io.grpc.SynchronizationContext; import io.grpc.internal.ObjectPool; +import io.grpc.xds.Bootstrapper.ServerInfo; import io.grpc.xds.CdsLoadBalancerProvider.CdsConfig; import io.grpc.xds.ClusterResolverLoadBalancerProvider.ClusterResolverConfig; import io.grpc.xds.ClusterResolverLoadBalancerProvider.ClusterResolverConfig.DiscoveryMechanism; @@ -75,7 +77,8 @@ public class CdsLoadBalancer2Test { private static final String CLUSTER = "cluster-foo.googleapis.com"; private static final String EDS_SERVICE_NAME = "backend-service-1.googleapis.com"; private static final String DNS_HOST_NAME = "backend-service-dns.googleapis.com:443"; - private static final String LRS_SERVER_NAME = "lrs.googleapis.com"; + private static final ServerInfo LRS_SERVER_INFO = + ServerInfo.create("lrs.googleapis.com", InsecureChannelCredentials.create(), true); private final UpstreamTlsContext upstreamTlsContext = CommonTlsContextTestsUtil.buildUpstreamTlsContext("google_cloud_private_spiffe", true); @@ -143,7 +146,7 @@ public void tearDown() { @Test public void discoverTopLevelEdsCluster() { CdsUpdate update = - CdsUpdate.forEds(CLUSTER, EDS_SERVICE_NAME, LRS_SERVER_NAME, 100L, upstreamTlsContext) + CdsUpdate.forEds(CLUSTER, EDS_SERVICE_NAME, LRS_SERVER_INFO, 100L, upstreamTlsContext) .roundRobinLbPolicy().build(); xdsClient.deliverCdsUpdate(CLUSTER, update); assertThat(childBalancers).hasSize(1); @@ -153,14 +156,14 @@ public void discoverTopLevelEdsCluster() { assertThat(childLbConfig.discoveryMechanisms).hasSize(1); DiscoveryMechanism instance = Iterables.getOnlyElement(childLbConfig.discoveryMechanisms); assertDiscoveryMechanism(instance, CLUSTER, DiscoveryMechanism.Type.EDS, EDS_SERVICE_NAME, - null, LRS_SERVER_NAME, 100L, upstreamTlsContext); + null, LRS_SERVER_INFO, 100L, upstreamTlsContext); assertThat(childLbConfig.lbPolicy.getProvider().getPolicyName()).isEqualTo("round_robin"); } @Test public void discoverTopLevelLogicalDnsCluster() { CdsUpdate update = - CdsUpdate.forLogicalDns(CLUSTER, DNS_HOST_NAME, LRS_SERVER_NAME, 100L, upstreamTlsContext) + CdsUpdate.forLogicalDns(CLUSTER, DNS_HOST_NAME, LRS_SERVER_INFO, 100L, upstreamTlsContext) .roundRobinLbPolicy().build(); xdsClient.deliverCdsUpdate(CLUSTER, update); assertThat(childBalancers).hasSize(1); @@ -170,7 +173,7 @@ public void discoverTopLevelLogicalDnsCluster() { assertThat(childLbConfig.discoveryMechanisms).hasSize(1); DiscoveryMechanism instance = Iterables.getOnlyElement(childLbConfig.discoveryMechanisms); assertDiscoveryMechanism(instance, CLUSTER, DiscoveryMechanism.Type.LOGICAL_DNS, null, - DNS_HOST_NAME, LRS_SERVER_NAME, 100L, upstreamTlsContext); + DNS_HOST_NAME, LRS_SERVER_INFO, 100L, upstreamTlsContext); assertThat(childLbConfig.lbPolicy.getProvider().getPolicyName()).isEqualTo("round_robin"); } @@ -198,13 +201,13 @@ public void nonAggregateCluster_resourceUpdate() { assertDiscoveryMechanism(instance, CLUSTER, DiscoveryMechanism.Type.EDS, null, null, null, 100L, upstreamTlsContext); - update = CdsUpdate.forEds(CLUSTER, EDS_SERVICE_NAME, LRS_SERVER_NAME, 200L, null) + update = CdsUpdate.forEds(CLUSTER, EDS_SERVICE_NAME, LRS_SERVER_INFO, 200L, null) .roundRobinLbPolicy().build(); xdsClient.deliverCdsUpdate(CLUSTER, update); childLbConfig = (ClusterResolverConfig) childBalancer.config; instance = Iterables.getOnlyElement(childLbConfig.discoveryMechanisms); assertDiscoveryMechanism(instance, CLUSTER, DiscoveryMechanism.Type.EDS, EDS_SERVICE_NAME, - null, LRS_SERVER_NAME, 200L, null); + null, LRS_SERVER_INFO, 200L, null); } @Test @@ -253,7 +256,7 @@ public void discoverAggregateCluster() { CLUSTER, cluster1, cluster2, cluster3, cluster4); assertThat(childBalancers).isEmpty(); CdsUpdate update3 = - CdsUpdate.forEds(cluster3, EDS_SERVICE_NAME, LRS_SERVER_NAME, 200L, upstreamTlsContext) + CdsUpdate.forEds(cluster3, EDS_SERVICE_NAME, LRS_SERVER_INFO, 200L, upstreamTlsContext) .roundRobinLbPolicy().build(); xdsClient.deliverCdsUpdate(cluster3, update3); assertThat(childBalancers).isEmpty(); @@ -263,7 +266,7 @@ public void discoverAggregateCluster() { xdsClient.deliverCdsUpdate(cluster2, update2); assertThat(childBalancers).isEmpty(); CdsUpdate update4 = - CdsUpdate.forEds(cluster4, null, LRS_SERVER_NAME, 300L, null) + CdsUpdate.forEds(cluster4, null, LRS_SERVER_INFO, 300L, null) .roundRobinLbPolicy().build(); xdsClient.deliverCdsUpdate(cluster4, update4); assertThat(childBalancers).hasSize(1); // all non-aggregate clusters discovered @@ -275,10 +278,10 @@ public void discoverAggregateCluster() { assertDiscoveryMechanism(childLbConfig.discoveryMechanisms.get(0), cluster2, DiscoveryMechanism.Type.LOGICAL_DNS, null, DNS_HOST_NAME, null, 100L, null); assertDiscoveryMechanism(childLbConfig.discoveryMechanisms.get(1), cluster3, - DiscoveryMechanism.Type.EDS, EDS_SERVICE_NAME, null, LRS_SERVER_NAME, 200L, + DiscoveryMechanism.Type.EDS, EDS_SERVICE_NAME, null, LRS_SERVER_INFO, 200L, upstreamTlsContext); assertDiscoveryMechanism(childLbConfig.discoveryMechanisms.get(2), cluster4, - DiscoveryMechanism.Type.EDS, null, null, LRS_SERVER_NAME, 300L, null); + DiscoveryMechanism.Type.EDS, null, null, LRS_SERVER_INFO, 300L, null); assertThat(childLbConfig.lbPolicy.getProvider().getPolicyName()) .isEqualTo("ring_hash"); // dominated by top-level cluster's config assertThat(((RingHashConfig) childLbConfig.lbPolicy.getConfig()).minRingSize).isEqualTo(100L); @@ -314,21 +317,21 @@ public void aggregateCluster_descendantClustersRevoked() { xdsClient.deliverCdsUpdate(CLUSTER, update); assertThat(xdsClient.watchers.keySet()).containsExactly(CLUSTER, cluster1, cluster2); CdsUpdate update1 = - CdsUpdate.forEds(cluster1, EDS_SERVICE_NAME, LRS_SERVER_NAME, 200L, upstreamTlsContext) + CdsUpdate.forEds(cluster1, EDS_SERVICE_NAME, LRS_SERVER_INFO, 200L, upstreamTlsContext) .roundRobinLbPolicy().build(); xdsClient.deliverCdsUpdate(cluster1, update1); CdsUpdate update2 = - CdsUpdate.forLogicalDns(cluster2, DNS_HOST_NAME, LRS_SERVER_NAME, 100L, null) + CdsUpdate.forLogicalDns(cluster2, DNS_HOST_NAME, LRS_SERVER_INFO, 100L, null) .roundRobinLbPolicy().build(); xdsClient.deliverCdsUpdate(cluster2, update2); FakeLoadBalancer childBalancer = Iterables.getOnlyElement(childBalancers); ClusterResolverConfig childLbConfig = (ClusterResolverConfig) childBalancer.config; assertThat(childLbConfig.discoveryMechanisms).hasSize(2); assertDiscoveryMechanism(childLbConfig.discoveryMechanisms.get(0), cluster1, - DiscoveryMechanism.Type.EDS, EDS_SERVICE_NAME, null, LRS_SERVER_NAME, 200L, + DiscoveryMechanism.Type.EDS, EDS_SERVICE_NAME, null, LRS_SERVER_INFO, 200L, upstreamTlsContext); assertDiscoveryMechanism(childLbConfig.discoveryMechanisms.get(1), cluster2, - DiscoveryMechanism.Type.LOGICAL_DNS, null, DNS_HOST_NAME, LRS_SERVER_NAME, 100L, null); + DiscoveryMechanism.Type.LOGICAL_DNS, null, DNS_HOST_NAME, LRS_SERVER_INFO, 100L, null); // Revoke cluster1, should still be able to proceed with cluster2. xdsClient.deliverResourceNotExist(cluster1); @@ -336,7 +339,7 @@ public void aggregateCluster_descendantClustersRevoked() { childLbConfig = (ClusterResolverConfig) childBalancer.config; assertThat(childLbConfig.discoveryMechanisms).hasSize(1); assertDiscoveryMechanism(Iterables.getOnlyElement(childLbConfig.discoveryMechanisms), cluster2, - DiscoveryMechanism.Type.LOGICAL_DNS, null, DNS_HOST_NAME, LRS_SERVER_NAME, 100L, null); + DiscoveryMechanism.Type.LOGICAL_DNS, null, DNS_HOST_NAME, LRS_SERVER_INFO, 100L, null); verify(helper, never()).updateBalancingState( eq(ConnectivityState.TRANSIENT_FAILURE), any(SubchannelPicker.class)); @@ -362,21 +365,21 @@ public void aggregateCluster_rootClusterRevoked() { xdsClient.deliverCdsUpdate(CLUSTER, update); assertThat(xdsClient.watchers.keySet()).containsExactly(CLUSTER, cluster1, cluster2); CdsUpdate update1 = - CdsUpdate.forEds(cluster1, EDS_SERVICE_NAME, LRS_SERVER_NAME, 200L, upstreamTlsContext) + CdsUpdate.forEds(cluster1, EDS_SERVICE_NAME, LRS_SERVER_INFO, 200L, upstreamTlsContext) .roundRobinLbPolicy().build(); xdsClient.deliverCdsUpdate(cluster1, update1); CdsUpdate update2 = - CdsUpdate.forLogicalDns(cluster2, DNS_HOST_NAME, LRS_SERVER_NAME, 100L, null) + CdsUpdate.forLogicalDns(cluster2, DNS_HOST_NAME, LRS_SERVER_INFO, 100L, null) .roundRobinLbPolicy().build(); xdsClient.deliverCdsUpdate(cluster2, update2); FakeLoadBalancer childBalancer = Iterables.getOnlyElement(childBalancers); ClusterResolverConfig childLbConfig = (ClusterResolverConfig) childBalancer.config; assertThat(childLbConfig.discoveryMechanisms).hasSize(2); assertDiscoveryMechanism(childLbConfig.discoveryMechanisms.get(0), cluster1, - DiscoveryMechanism.Type.EDS, EDS_SERVICE_NAME, null, LRS_SERVER_NAME, 200L, + DiscoveryMechanism.Type.EDS, EDS_SERVICE_NAME, null, LRS_SERVER_INFO, 200L, upstreamTlsContext); assertDiscoveryMechanism(childLbConfig.discoveryMechanisms.get(1), cluster2, - DiscoveryMechanism.Type.LOGICAL_DNS, null, DNS_HOST_NAME, LRS_SERVER_NAME, 100L, null); + DiscoveryMechanism.Type.LOGICAL_DNS, null, DNS_HOST_NAME, LRS_SERVER_INFO, 100L, null); xdsClient.deliverResourceNotExist(CLUSTER); assertThat(xdsClient.watchers.keySet()) @@ -416,7 +419,7 @@ public void aggregateCluster_intermediateClusterChanges() { xdsClient.deliverCdsUpdate(cluster2, update2); assertThat(xdsClient.watchers.keySet()).containsExactly(CLUSTER, cluster2, cluster3); CdsUpdate update3 = - CdsUpdate.forEds(cluster3, EDS_SERVICE_NAME, LRS_SERVER_NAME, 100L, upstreamTlsContext) + CdsUpdate.forEds(cluster3, EDS_SERVICE_NAME, LRS_SERVER_INFO, 100L, upstreamTlsContext) .roundRobinLbPolicy().build(); xdsClient.deliverCdsUpdate(cluster3, update3); FakeLoadBalancer childBalancer = Iterables.getOnlyElement(childBalancers); @@ -424,7 +427,7 @@ public void aggregateCluster_intermediateClusterChanges() { assertThat(childLbConfig.discoveryMechanisms).hasSize(1); DiscoveryMechanism instance = Iterables.getOnlyElement(childLbConfig.discoveryMechanisms); assertDiscoveryMechanism(instance, cluster3, DiscoveryMechanism.Type.EDS, EDS_SERVICE_NAME, - null, LRS_SERVER_NAME, 100L, upstreamTlsContext); + null, LRS_SERVER_INFO, 100L, upstreamTlsContext); // cluster2 revoked xdsClient.deliverResourceNotExist(cluster2); @@ -465,7 +468,7 @@ public void aggregateCluster_discoveryErrorAfterChildLbCreated_propagateToChildL .roundRobinLbPolicy().build(); xdsClient.deliverCdsUpdate(CLUSTER, update); CdsUpdate update1 = - CdsUpdate.forLogicalDns(cluster1, DNS_HOST_NAME, LRS_SERVER_NAME, 200L, null) + CdsUpdate.forLogicalDns(cluster1, DNS_HOST_NAME, LRS_SERVER_INFO, 200L, null) .roundRobinLbPolicy().build(); xdsClient.deliverCdsUpdate(cluster1, update1); FakeLoadBalancer childLb = Iterables.getOnlyElement(childBalancers); @@ -490,7 +493,7 @@ public void handleNameResolutionErrorFromUpstream_beforeChildLbCreated_returnErr @Test public void handleNameResolutionErrorFromUpstream_afterChildLbCreated_fallThrough() { CdsUpdate update = - CdsUpdate.forEds(CLUSTER, EDS_SERVICE_NAME, LRS_SERVER_NAME, 100L, upstreamTlsContext) + CdsUpdate.forEds(CLUSTER, EDS_SERVICE_NAME, LRS_SERVER_INFO, 100L, upstreamTlsContext) .roundRobinLbPolicy().build(); xdsClient.deliverCdsUpdate(CLUSTER, update); FakeLoadBalancer childBalancer = Iterables.getOnlyElement(childBalancers); @@ -515,13 +518,13 @@ private static void assertPicker(SubchannelPicker picker, Status expectedStatus, private static void assertDiscoveryMechanism(DiscoveryMechanism instance, String name, DiscoveryMechanism.Type type, @Nullable String edsServiceName, @Nullable String dnsHostName, - @Nullable String lrsServerName, @Nullable Long maxConcurrentRequests, + @Nullable ServerInfo lrsServerInfo, @Nullable Long maxConcurrentRequests, @Nullable UpstreamTlsContext tlsContext) { assertThat(instance.cluster).isEqualTo(name); assertThat(instance.type).isEqualTo(type); assertThat(instance.edsServiceName).isEqualTo(edsServiceName); assertThat(instance.dnsHostName).isEqualTo(dnsHostName); - assertThat(instance.lrsServerName).isEqualTo(lrsServerName); + assertThat(instance.lrsServerInfo).isEqualTo(lrsServerInfo); assertThat(instance.maxConcurrentRequests).isEqualTo(maxConcurrentRequests); assertThat(instance.tlsContext).isEqualTo(tlsContext); } diff --git a/xds/src/test/java/io/grpc/xds/ClientXdsClientDataTest.java b/xds/src/test/java/io/grpc/xds/ClientXdsClientDataTest.java index 2dbab1bcb9d..cb7b937bb5f 100644 --- a/xds/src/test/java/io/grpc/xds/ClientXdsClientDataTest.java +++ b/xds/src/test/java/io/grpc/xds/ClientXdsClientDataTest.java @@ -94,7 +94,9 @@ import io.envoyproxy.envoy.type.v3.FractionalPercent; import io.envoyproxy.envoy.type.v3.FractionalPercent.DenominatorType; import io.envoyproxy.envoy.type.v3.Int64Range; +import io.grpc.InsecureChannelCredentials; import io.grpc.Status.Code; +import io.grpc.xds.Bootstrapper.ServerInfo; import io.grpc.xds.ClientXdsClient.ResourceInvalidException; import io.grpc.xds.ClientXdsClient.StructOrError; import io.grpc.xds.Endpoints.LbEndpoint; @@ -126,6 +128,9 @@ @RunWith(JUnit4.class) public class ClientXdsClientDataTest { + private static final ServerInfo LRS_SERVER_INFO = + ServerInfo.create("lrs.googleapis.com", InsecureChannelCredentials.create(), true); + @SuppressWarnings("deprecation") // https://github.com/grpc/grpc-java/issues/7467 @Rule public final ExpectedException thrown = ExpectedException.none(); @@ -1239,7 +1244,8 @@ public void parseCluster_ringHashLbPolicy_defaultLbConfig() throws ResourceInval .setLbPolicy(LbPolicy.RING_HASH) .build(); - CdsUpdate update = ClientXdsClient.parseCluster(cluster, new HashSet(), null); + CdsUpdate update = ClientXdsClient.parseCluster( + cluster, new HashSet(), null, LRS_SERVER_INFO); assertThat(update.lbPolicy()).isEqualTo(CdsUpdate.LbPolicy.RING_HASH); assertThat(update.minRingSize()) .isEqualTo(ClientXdsClient.DEFAULT_RING_HASH_LB_POLICY_MIN_RING_SIZE); @@ -1266,7 +1272,7 @@ public void parseCluster_transportSocketMatches_exception() throws ResourceInval thrown.expect(ResourceInvalidException.class); thrown.expectMessage( "Cluster cluster-foo.googleapis.com: transport-socket-matches not supported."); - ClientXdsClient.parseCluster(cluster, new HashSet(), null); + ClientXdsClient.parseCluster(cluster, new HashSet(), null, LRS_SERVER_INFO); } @Test @@ -1291,7 +1297,7 @@ public void parseCluster_ringHashLbPolicy_invalidRingSizeConfig_minGreaterThanMa thrown.expect(ResourceInvalidException.class); thrown.expectMessage("Cluster cluster-foo.googleapis.com: invalid ring_hash_lb_config"); - ClientXdsClient.parseCluster(cluster, new HashSet(), null); + ClientXdsClient.parseCluster(cluster, new HashSet(), null, LRS_SERVER_INFO); } @Test @@ -1318,7 +1324,7 @@ public void parseCluster_ringHashLbPolicy_invalidRingSizeConfig_tooLargeRingSize thrown.expect(ResourceInvalidException.class); thrown.expectMessage("Cluster cluster-foo.googleapis.com: invalid ring_hash_lb_config"); - ClientXdsClient.parseCluster(cluster, new HashSet(), null); + ClientXdsClient.parseCluster(cluster, new HashSet(), null, LRS_SERVER_INFO); } @Test diff --git a/xds/src/test/java/io/grpc/xds/ClientXdsClientTestBase.java b/xds/src/test/java/io/grpc/xds/ClientXdsClientTestBase.java index 4a34538954c..9d0c735ba95 100644 --- a/xds/src/test/java/io/grpc/xds/ClientXdsClientTestBase.java +++ b/xds/src/test/java/io/grpc/xds/ClientXdsClientTestBase.java @@ -43,6 +43,7 @@ import io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.CertificateProviderPluginInstance; import io.envoyproxy.envoy.extensions.transport_sockets.tls.v3.CommonTlsContext; import io.grpc.BindableService; +import io.grpc.ChannelCredentials; import io.grpc.Context; import io.grpc.Context.CancellableContext; import io.grpc.InsecureChannelCredentials; @@ -124,6 +125,9 @@ public abstract class ClientXdsClientTestBase { private static final String VERSION_3 = "44"; private static final Node NODE = Node.newBuilder().build(); private static final Any FAILING_ANY = MessageFactory.FAILING_ANY; + private static final ChannelCredentials CHANNEL_CREDENTIALS = InsecureChannelCredentials.create(); + private final ServerInfo lrsServerInfo = + ServerInfo.create(SERVER_URI, CHANNEL_CREDENTIALS, useProtocolV3()); private static final FakeClock.TaskFilter RPC_RETRY_TASK_FILTER = new FakeClock.TaskFilter() { @@ -284,8 +288,7 @@ ManagedChannel create(ServerInfo serverInfo) { Bootstrapper.BootstrapInfo bootstrapInfo = Bootstrapper.BootstrapInfo.builder() .servers(Arrays.asList( - Bootstrapper.ServerInfo.create( - SERVER_URI, InsecureChannelCredentials.create(), useProtocolV3()))) + Bootstrapper.ServerInfo.create(SERVER_URI, CHANNEL_CREDENTIALS, useProtocolV3()))) .node(EnvoyProtoData.Node.newBuilder().build()) .certProviders(ImmutableMap.of("cert-instance-name", CertificateProviderInfo.create("file-watcher", ImmutableMap.of()))) @@ -1393,7 +1396,7 @@ public void cdsResourceFound() { assertThat(cdsUpdate.clusterType()).isEqualTo(ClusterType.EDS); assertThat(cdsUpdate.edsServiceName()).isNull(); assertThat(cdsUpdate.lbPolicy()).isEqualTo(LbPolicy.ROUND_ROBIN); - assertThat(cdsUpdate.lrsServerName()).isNull(); + assertThat(cdsUpdate.lrsServerInfo()).isNull(); assertThat(cdsUpdate.maxConcurrentRequests()).isNull(); assertThat(cdsUpdate.upstreamTlsContext()).isNull(); assertThat(fakeClock.getPendingTasks(CDS_RESOURCE_FETCH_TIMEOUT_TASK_FILTER)).isEmpty(); @@ -1422,7 +1425,7 @@ public void cdsResourceFound_ringHashLbPolicy() { assertThat(cdsUpdate.lbPolicy()).isEqualTo(LbPolicy.RING_HASH); assertThat(cdsUpdate.minRingSize()).isEqualTo(10L); assertThat(cdsUpdate.maxRingSize()).isEqualTo(100L); - assertThat(cdsUpdate.lrsServerName()).isNull(); + assertThat(cdsUpdate.lrsServerInfo()).isNull(); assertThat(cdsUpdate.maxConcurrentRequests()).isNull(); assertThat(cdsUpdate.upstreamTlsContext()).isNull(); assertThat(fakeClock.getPendingTasks(CDS_RESOURCE_FETCH_TIMEOUT_TASK_FILTER)).isEmpty(); @@ -1467,7 +1470,7 @@ public void cdsResponseWithCircuitBreakers() { assertThat(cdsUpdate.clusterType()).isEqualTo(ClusterType.EDS); assertThat(cdsUpdate.edsServiceName()).isNull(); assertThat(cdsUpdate.lbPolicy()).isEqualTo(LbPolicy.ROUND_ROBIN); - assertThat(cdsUpdate.lrsServerName()).isNull(); + assertThat(cdsUpdate.lrsServerInfo()).isNull(); assertThat(cdsUpdate.maxConcurrentRequests()).isEqualTo(200L); assertThat(cdsUpdate.upstreamTlsContext()).isNull(); verifyResourceMetadataAcked(CDS, CDS_RESOURCE, clusterCircuitBreakers, VERSION_1, @@ -1617,7 +1620,7 @@ public void cachedCdsResource_data() { assertThat(cdsUpdate.clusterType()).isEqualTo(ClusterType.EDS); assertThat(cdsUpdate.edsServiceName()).isNull(); assertThat(cdsUpdate.lbPolicy()).isEqualTo(LbPolicy.ROUND_ROBIN); - assertThat(cdsUpdate.lrsServerName()).isNull(); + assertThat(cdsUpdate.lrsServerInfo()).isNull(); assertThat(cdsUpdate.maxConcurrentRequests()).isNull(); assertThat(cdsUpdate.upstreamTlsContext()).isNull(); call.verifyNoMoreRequest(); @@ -1659,7 +1662,7 @@ public void cdsResourceUpdated() { assertThat(cdsUpdate.clusterType()).isEqualTo(ClusterType.LOGICAL_DNS); assertThat(cdsUpdate.dnsHostName()).isEqualTo(dnsHostAddr + ":" + dnsHostPort); assertThat(cdsUpdate.lbPolicy()).isEqualTo(LbPolicy.ROUND_ROBIN); - assertThat(cdsUpdate.lrsServerName()).isNull(); + assertThat(cdsUpdate.lrsServerInfo()).isNull(); assertThat(cdsUpdate.maxConcurrentRequests()).isNull(); assertThat(cdsUpdate.upstreamTlsContext()).isNull(); verifyResourceMetadataAcked(CDS, CDS_RESOURCE, clusterDns, VERSION_1, TIME_INCREMENT); @@ -1678,7 +1681,7 @@ public void cdsResourceUpdated() { assertThat(cdsUpdate.clusterType()).isEqualTo(ClusterType.EDS); assertThat(cdsUpdate.edsServiceName()).isEqualTo(edsService); assertThat(cdsUpdate.lbPolicy()).isEqualTo(LbPolicy.ROUND_ROBIN); - assertThat(cdsUpdate.lrsServerName()).isEqualTo(""); + assertThat(cdsUpdate.lrsServerInfo()).isEqualTo(lrsServerInfo); assertThat(cdsUpdate.maxConcurrentRequests()).isNull(); assertThat(cdsUpdate.upstreamTlsContext()).isNull(); verifyResourceMetadataAcked(CDS, CDS_RESOURCE, clusterEds, VERSION_2, TIME_INCREMENT * 2); @@ -1699,7 +1702,7 @@ public void cdsResourceDeleted() { assertThat(cdsUpdate.clusterType()).isEqualTo(ClusterType.EDS); assertThat(cdsUpdate.edsServiceName()).isNull(); assertThat(cdsUpdate.lbPolicy()).isEqualTo(LbPolicy.ROUND_ROBIN); - assertThat(cdsUpdate.lrsServerName()).isNull(); + assertThat(cdsUpdate.lrsServerInfo()).isNull(); assertThat(cdsUpdate.maxConcurrentRequests()).isNull(); assertThat(cdsUpdate.upstreamTlsContext()).isNull(); verifyResourceMetadataAcked(CDS, CDS_RESOURCE, testClusterRoundRobin, VERSION_1, @@ -1751,7 +1754,7 @@ public void multipleCdsWatchers() { assertThat(cdsUpdate.clusterType()).isEqualTo(ClusterType.LOGICAL_DNS); assertThat(cdsUpdate.dnsHostName()).isEqualTo(dnsHostAddr + ":" + dnsHostPort); assertThat(cdsUpdate.lbPolicy()).isEqualTo(LbPolicy.ROUND_ROBIN); - assertThat(cdsUpdate.lrsServerName()).isNull(); + assertThat(cdsUpdate.lrsServerInfo()).isNull(); assertThat(cdsUpdate.maxConcurrentRequests()).isNull(); assertThat(cdsUpdate.upstreamTlsContext()).isNull(); verify(watcher1).onChanged(cdsUpdateCaptor.capture()); @@ -1760,7 +1763,7 @@ public void multipleCdsWatchers() { assertThat(cdsUpdate.clusterType()).isEqualTo(ClusterType.EDS); assertThat(cdsUpdate.edsServiceName()).isEqualTo(edsService); assertThat(cdsUpdate.lbPolicy()).isEqualTo(LbPolicy.ROUND_ROBIN); - assertThat(cdsUpdate.lrsServerName()).isEqualTo(""); + assertThat(cdsUpdate.lrsServerInfo()).isEqualTo(lrsServerInfo); assertThat(cdsUpdate.maxConcurrentRequests()).isNull(); assertThat(cdsUpdate.upstreamTlsContext()).isNull(); verify(watcher2).onChanged(cdsUpdateCaptor.capture()); @@ -1769,7 +1772,7 @@ public void multipleCdsWatchers() { assertThat(cdsUpdate.clusterType()).isEqualTo(ClusterType.EDS); assertThat(cdsUpdate.edsServiceName()).isEqualTo(edsService); assertThat(cdsUpdate.lbPolicy()).isEqualTo(LbPolicy.ROUND_ROBIN); - assertThat(cdsUpdate.lrsServerName()).isEqualTo(""); + assertThat(cdsUpdate.lrsServerInfo()).isEqualTo(lrsServerInfo); assertThat(cdsUpdate.maxConcurrentRequests()).isNull(); assertThat(cdsUpdate.upstreamTlsContext()).isNull(); // Metadata of both clusters is stored. @@ -2013,11 +2016,11 @@ public void edsResourceDeletedByCds() { verify(cdsWatcher).onChanged(cdsUpdateCaptor.capture()); CdsUpdate cdsUpdate = cdsUpdateCaptor.getValue(); assertThat(cdsUpdate.edsServiceName()).isEqualTo(null); - assertThat(cdsUpdate.lrsServerName()).isEqualTo(""); + assertThat(cdsUpdate.lrsServerInfo()).isEqualTo(lrsServerInfo); verify(cdsResourceWatcher).onChanged(cdsUpdateCaptor.capture()); cdsUpdate = cdsUpdateCaptor.getValue(); assertThat(cdsUpdate.edsServiceName()).isEqualTo(EDS_RESOURCE); - assertThat(cdsUpdate.lrsServerName()).isNull(); + assertThat(cdsUpdate.lrsServerInfo()).isNull(); verifyResourceMetadataAcked(CDS, resource, clusters.get(0), VERSION_1, TIME_INCREMENT); verifyResourceMetadataAcked(CDS, CDS_RESOURCE, clusters.get(1), VERSION_1, TIME_INCREMENT); verifyResourceMetadataRequested(EDS, EDS_RESOURCE); @@ -2343,7 +2346,7 @@ public void streamClosedAndRetryRestartsResourceInitialFetchTimerForUnresolvedRe public void reportLoadStatsToServer() { xdsClient.watchLdsResource(LDS_RESOURCE, ldsResourceWatcher); String clusterName = "cluster-foo.googleapis.com"; - ClusterDropStats dropStats = xdsClient.addClusterDropStats(clusterName, null); + ClusterDropStats dropStats = xdsClient.addClusterDropStats(lrsServerInfo, clusterName, null); LrsRpcCall lrsCall = loadReportCalls.poll(); lrsCall.verifyNextReportClusters(Collections.emptyList()); // initial LRS request diff --git a/xds/src/test/java/io/grpc/xds/ClusterImplLoadBalancerTest.java b/xds/src/test/java/io/grpc/xds/ClusterImplLoadBalancerTest.java index dfcf101fcf5..582747aecb7 100644 --- a/xds/src/test/java/io/grpc/xds/ClusterImplLoadBalancerTest.java +++ b/xds/src/test/java/io/grpc/xds/ClusterImplLoadBalancerTest.java @@ -27,6 +27,7 @@ import io.grpc.ClientStreamTracer; import io.grpc.ConnectivityState; import io.grpc.EquivalentAddressGroup; +import io.grpc.InsecureChannelCredentials; import io.grpc.LoadBalancer; import io.grpc.LoadBalancer.CreateSubchannelArgs; import io.grpc.LoadBalancer.Helper; @@ -44,6 +45,7 @@ import io.grpc.internal.FakeClock; import io.grpc.internal.ObjectPool; import io.grpc.internal.ServiceConfigUtil.PolicySelection; +import io.grpc.xds.Bootstrapper.ServerInfo; import io.grpc.xds.ClusterImplLoadBalancerProvider.ClusterImplConfig; import io.grpc.xds.Endpoints.DropOverload; import io.grpc.xds.EnvoyServerProtoData.DownstreamTlsContext; @@ -85,7 +87,8 @@ public class ClusterImplLoadBalancerTest { private static final String AUTHORITY = "api.google.com"; private static final String CLUSTER = "cluster-foo.googleapis.com"; private static final String EDS_SERVICE_NAME = "service.googleapis.com"; - private static final String LRS_SERVER_NAME = ""; + private static final ServerInfo LRS_SERVER_INFO = + ServerInfo.create("api.google.com", InsecureChannelCredentials.create(), true); private final SynchronizationContext syncContext = new SynchronizationContext( new Thread.UncaughtExceptionHandler() { @Override @@ -150,7 +153,7 @@ public void handleResolvedAddresses_propagateToChildPolicy() { FakeLoadBalancerProvider weightedTargetProvider = new FakeLoadBalancerProvider(XdsLbPolicies.WEIGHTED_TARGET_POLICY_NAME); Object weightedTargetConfig = new Object(); - ClusterImplConfig config = new ClusterImplConfig(CLUSTER, EDS_SERVICE_NAME, LRS_SERVER_NAME, + ClusterImplConfig config = new ClusterImplConfig(CLUSTER, EDS_SERVICE_NAME, LRS_SERVER_INFO, null, Collections.emptyList(), new PolicySelection(weightedTargetProvider, weightedTargetConfig), null); EquivalentAddressGroup endpoint = makeAddress("endpoint-addr", locality); @@ -177,7 +180,7 @@ public void nameResolutionError_afterChildPolicyInstantiated_propagateToDownstre FakeLoadBalancerProvider weightedTargetProvider = new FakeLoadBalancerProvider(XdsLbPolicies.WEIGHTED_TARGET_POLICY_NAME); Object weightedTargetConfig = new Object(); - ClusterImplConfig config = new ClusterImplConfig(CLUSTER, EDS_SERVICE_NAME, LRS_SERVER_NAME, + ClusterImplConfig config = new ClusterImplConfig(CLUSTER, EDS_SERVICE_NAME, LRS_SERVER_INFO, null, Collections.emptyList(), new PolicySelection(weightedTargetProvider, weightedTargetConfig), null); EquivalentAddressGroup endpoint = makeAddress("endpoint-addr", locality); @@ -196,7 +199,7 @@ public void recordLoadStats() { LoadBalancerProvider weightedTargetProvider = new WeightedTargetLoadBalancerProvider(); WeightedTargetConfig weightedTargetConfig = buildWeightedTargetConfig(ImmutableMap.of(locality, 10)); - ClusterImplConfig config = new ClusterImplConfig(CLUSTER, EDS_SERVICE_NAME, LRS_SERVER_NAME, + ClusterImplConfig config = new ClusterImplConfig(CLUSTER, EDS_SERVICE_NAME, LRS_SERVER_INFO, null, Collections.emptyList(), new PolicySelection(weightedTargetProvider, weightedTargetConfig), null); EquivalentAddressGroup endpoint = makeAddress("endpoint-addr", locality); @@ -247,7 +250,7 @@ public void dropRpcsWithRespectToLbConfigDropCategories() { LoadBalancerProvider weightedTargetProvider = new WeightedTargetLoadBalancerProvider(); WeightedTargetConfig weightedTargetConfig = buildWeightedTargetConfig(ImmutableMap.of(locality, 10)); - ClusterImplConfig config = new ClusterImplConfig(CLUSTER, EDS_SERVICE_NAME, LRS_SERVER_NAME, + ClusterImplConfig config = new ClusterImplConfig(CLUSTER, EDS_SERVICE_NAME, LRS_SERVER_INFO, null, Collections.singletonList(DropOverload.create("throttle", 500_000)), new PolicySelection(weightedTargetProvider, weightedTargetConfig), null); EquivalentAddressGroup endpoint = makeAddress("endpoint-addr", locality); @@ -276,7 +279,7 @@ public void dropRpcsWithRespectToLbConfigDropCategories() { assertThat(clusterStats.totalDroppedRequests()).isEqualTo(1L); // Config update updates drop policies. - config = new ClusterImplConfig(CLUSTER, EDS_SERVICE_NAME, LRS_SERVER_NAME, null, + config = new ClusterImplConfig(CLUSTER, EDS_SERVICE_NAME, LRS_SERVER_INFO, null, Collections.singletonList(DropOverload.create("lb", 1_000_000)), new PolicySelection(weightedTargetProvider, weightedTargetConfig), null); loadBalancer.handleResolvedAddresses( @@ -323,7 +326,7 @@ private void subtest_maxConcurrentRequests_appliedByLbConfig(boolean enableCircu LoadBalancerProvider weightedTargetProvider = new WeightedTargetLoadBalancerProvider(); WeightedTargetConfig weightedTargetConfig = buildWeightedTargetConfig(ImmutableMap.of(locality, 10)); - ClusterImplConfig config = new ClusterImplConfig(CLUSTER, EDS_SERVICE_NAME, LRS_SERVER_NAME, + ClusterImplConfig config = new ClusterImplConfig(CLUSTER, EDS_SERVICE_NAME, LRS_SERVER_INFO, maxConcurrentRequests, Collections.emptyList(), new PolicySelection(weightedTargetProvider, weightedTargetConfig), null); EquivalentAddressGroup endpoint = makeAddress("endpoint-addr", locality); @@ -365,7 +368,7 @@ private void subtest_maxConcurrentRequests_appliedByLbConfig(boolean enableCircu // Config update increments circuit breakers max_concurrent_requests threshold. maxConcurrentRequests = 101L; - config = new ClusterImplConfig(CLUSTER, EDS_SERVICE_NAME, LRS_SERVER_NAME, + config = new ClusterImplConfig(CLUSTER, EDS_SERVICE_NAME, LRS_SERVER_INFO, maxConcurrentRequests, Collections.emptyList(), new PolicySelection(weightedTargetProvider, weightedTargetConfig), null); deliverAddressesAndConfig(Collections.singletonList(endpoint), config); @@ -411,7 +414,7 @@ private void subtest_maxConcurrentRequests_appliedWithDefaultValue( LoadBalancerProvider weightedTargetProvider = new WeightedTargetLoadBalancerProvider(); WeightedTargetConfig weightedTargetConfig = buildWeightedTargetConfig(ImmutableMap.of(locality, 10)); - ClusterImplConfig config = new ClusterImplConfig(CLUSTER, EDS_SERVICE_NAME, LRS_SERVER_NAME, + ClusterImplConfig config = new ClusterImplConfig(CLUSTER, EDS_SERVICE_NAME, LRS_SERVER_INFO, null, Collections.emptyList(), new PolicySelection(weightedTargetProvider, weightedTargetConfig), null); EquivalentAddressGroup endpoint = makeAddress("endpoint-addr", locality); @@ -457,7 +460,7 @@ public void endpointAddressesAttachedWithClusterName() { LoadBalancerProvider weightedTargetProvider = new WeightedTargetLoadBalancerProvider(); WeightedTargetConfig weightedTargetConfig = buildWeightedTargetConfig(ImmutableMap.of(locality, 10)); - ClusterImplConfig config = new ClusterImplConfig(CLUSTER, EDS_SERVICE_NAME, LRS_SERVER_NAME, + ClusterImplConfig config = new ClusterImplConfig(CLUSTER, EDS_SERVICE_NAME, LRS_SERVER_INFO, null, Collections.emptyList(), new PolicySelection(weightedTargetProvider, weightedTargetConfig), null); // One locality with two endpoints. @@ -498,7 +501,7 @@ private void subtest_endpointAddressesAttachedWithTlsConfig(boolean enableSecuri LoadBalancerProvider weightedTargetProvider = new WeightedTargetLoadBalancerProvider(); WeightedTargetConfig weightedTargetConfig = buildWeightedTargetConfig(ImmutableMap.of(locality, 10)); - ClusterImplConfig config = new ClusterImplConfig(CLUSTER, EDS_SERVICE_NAME, LRS_SERVER_NAME, + ClusterImplConfig config = new ClusterImplConfig(CLUSTER, EDS_SERVICE_NAME, LRS_SERVER_INFO, null, Collections.emptyList(), new PolicySelection(weightedTargetProvider, weightedTargetConfig), upstreamTlsContext); // One locality with two endpoints. @@ -525,7 +528,7 @@ private void subtest_endpointAddressesAttachedWithTlsConfig(boolean enableSecuri } // Removes UpstreamTlsContext from the config. - config = new ClusterImplConfig(CLUSTER, EDS_SERVICE_NAME, LRS_SERVER_NAME, + config = new ClusterImplConfig(CLUSTER, EDS_SERVICE_NAME, LRS_SERVER_INFO, null, Collections.emptyList(), new PolicySelection(weightedTargetProvider, weightedTargetConfig), null); deliverAddressesAndConfig(Arrays.asList(endpoint1, endpoint2), config); @@ -539,7 +542,7 @@ private void subtest_endpointAddressesAttachedWithTlsConfig(boolean enableSecuri // Config with a new UpstreamTlsContext. upstreamTlsContext = CommonTlsContextTestsUtil.buildUpstreamTlsContext("google_cloud_private_spiffe1", true); - config = new ClusterImplConfig(CLUSTER, EDS_SERVICE_NAME, LRS_SERVER_NAME, + config = new ClusterImplConfig(CLUSTER, EDS_SERVICE_NAME, LRS_SERVER_INFO, null, Collections.emptyList(), new PolicySelection(weightedTargetProvider, weightedTargetConfig), upstreamTlsContext); deliverAddressesAndConfig(Arrays.asList(endpoint1, endpoint2), config); @@ -761,13 +764,15 @@ public Attributes getAttributes() { private final class FakeXdsClient extends XdsClient { @Override - ClusterDropStats addClusterDropStats(String clusterName, @Nullable String edsServiceName) { + ClusterDropStats addClusterDropStats( + ServerInfo lrsServerInfo, String clusterName, @Nullable String edsServiceName) { return loadStatsManager.getClusterDropStats(clusterName, edsServiceName); } @Override - ClusterLocalityStats addClusterLocalityStats(String clusterName, - @Nullable String edsServiceName, Locality locality) { + ClusterLocalityStats addClusterLocalityStats( + ServerInfo lrsServerInfo, String clusterName, @Nullable String edsServiceName, + Locality locality) { return loadStatsManager.getClusterLocalityStats(clusterName, edsServiceName, locality); } diff --git a/xds/src/test/java/io/grpc/xds/ClusterResolverLoadBalancerTest.java b/xds/src/test/java/io/grpc/xds/ClusterResolverLoadBalancerTest.java index 26f1a2b3cf7..ccc1975265d 100644 --- a/xds/src/test/java/io/grpc/xds/ClusterResolverLoadBalancerTest.java +++ b/xds/src/test/java/io/grpc/xds/ClusterResolverLoadBalancerTest.java @@ -34,6 +34,7 @@ import io.grpc.ChannelLogger; import io.grpc.ConnectivityState; import io.grpc.EquivalentAddressGroup; +import io.grpc.InsecureChannelCredentials; import io.grpc.LoadBalancer; import io.grpc.LoadBalancer.Helper; import io.grpc.LoadBalancer.PickResult; @@ -56,6 +57,7 @@ import io.grpc.internal.GrpcUtil; import io.grpc.internal.ObjectPool; import io.grpc.internal.ServiceConfigUtil.PolicySelection; +import io.grpc.xds.Bootstrapper.ServerInfo; import io.grpc.xds.ClusterImplLoadBalancerProvider.ClusterImplConfig; import io.grpc.xds.ClusterResolverLoadBalancerProvider.ClusterResolverConfig; import io.grpc.xds.ClusterResolverLoadBalancerProvider.ClusterResolverConfig.DiscoveryMechanism; @@ -103,7 +105,8 @@ public class ClusterResolverLoadBalancerTest { private static final String EDS_SERVICE_NAME1 = "backend-service-foo.googleapis.com"; private static final String EDS_SERVICE_NAME2 = "backend-service-bar.googleapis.com"; private static final String DNS_HOST_NAME = "dns-service.googleapis.com"; - private static final String LRS_SERVER_NAME = "lrs.googleapis.com"; + private static final ServerInfo LRS_SERVER_INFO = + ServerInfo.create("lrs.googleapis.com", InsecureChannelCredentials.create(), true); private final Locality locality1 = Locality.create("test-region-1", "test-zone-1", "test-subzone-1"); private final Locality locality2 = @@ -113,11 +116,11 @@ public class ClusterResolverLoadBalancerTest { private final UpstreamTlsContext tlsContext = CommonTlsContextTestsUtil.buildUpstreamTlsContext("google_cloud_private_spiffe", true); private final DiscoveryMechanism edsDiscoveryMechanism1 = - DiscoveryMechanism.forEds(CLUSTER1, EDS_SERVICE_NAME1, LRS_SERVER_NAME, 100L, tlsContext); + DiscoveryMechanism.forEds(CLUSTER1, EDS_SERVICE_NAME1, LRS_SERVER_INFO, 100L, tlsContext); private final DiscoveryMechanism edsDiscoveryMechanism2 = - DiscoveryMechanism.forEds(CLUSTER2, EDS_SERVICE_NAME2, LRS_SERVER_NAME, 200L, tlsContext); + DiscoveryMechanism.forEds(CLUSTER2, EDS_SERVICE_NAME2, LRS_SERVER_INFO, 200L, tlsContext); private final DiscoveryMechanism logicalDnsDiscoveryMechanism = - DiscoveryMechanism.forLogicalDns(CLUSTER_DNS, DNS_HOST_NAME, LRS_SERVER_NAME, 300L, null); + DiscoveryMechanism.forLogicalDns(CLUSTER_DNS, DNS_HOST_NAME, LRS_SERVER_INFO, 300L, null); private final SynchronizationContext syncContext = new SynchronizationContext( new Thread.UncaughtExceptionHandler() { @@ -256,7 +259,7 @@ public void edsClustersWithRingHashEndpointLbPolicy() { .isEqualTo(CLUSTER_IMPL_POLICY_NAME); ClusterImplConfig clusterImplConfig = (ClusterImplConfig) priorityChildConfig.policySelection.getConfig(); - assertClusterImplConfig(clusterImplConfig, CLUSTER1, EDS_SERVICE_NAME1, LRS_SERVER_NAME, 100L, + assertClusterImplConfig(clusterImplConfig, CLUSTER1, EDS_SERVICE_NAME1, LRS_SERVER_INFO, 100L, tlsContext, Collections.emptyList(), "ring_hash"); RingHashConfig ringHashConfig = (RingHashConfig) clusterImplConfig.childPolicy.getConfig(); @@ -320,7 +323,7 @@ public void onlyEdsClusters_receivedEndpoints() { .isEqualTo(CLUSTER_IMPL_POLICY_NAME); ClusterImplConfig clusterImplConfig1 = (ClusterImplConfig) priorityChildConfig1.policySelection.getConfig(); - assertClusterImplConfig(clusterImplConfig1, CLUSTER2, EDS_SERVICE_NAME2, LRS_SERVER_NAME, 200L, + assertClusterImplConfig(clusterImplConfig1, CLUSTER2, EDS_SERVICE_NAME2, LRS_SERVER_INFO, 200L, tlsContext, Collections.emptyList(), WEIGHTED_TARGET_POLICY_NAME); WeightedTargetConfig weightedTargetConfig1 = (WeightedTargetConfig) clusterImplConfig1.childPolicy.getConfig(); @@ -335,7 +338,7 @@ public void onlyEdsClusters_receivedEndpoints() { .isEqualTo(CLUSTER_IMPL_POLICY_NAME); ClusterImplConfig clusterImplConfig2 = (ClusterImplConfig) priorityChildConfig2.policySelection.getConfig(); - assertClusterImplConfig(clusterImplConfig2, CLUSTER2, EDS_SERVICE_NAME2, LRS_SERVER_NAME, 200L, + assertClusterImplConfig(clusterImplConfig2, CLUSTER2, EDS_SERVICE_NAME2, LRS_SERVER_INFO, 200L, tlsContext, Collections.emptyList(), WEIGHTED_TARGET_POLICY_NAME); WeightedTargetConfig weightedTargetConfig2 = (WeightedTargetConfig) clusterImplConfig2.childPolicy.getConfig(); @@ -358,7 +361,7 @@ public void onlyEdsClusters_receivedEndpoints() { .isEqualTo(CLUSTER_IMPL_POLICY_NAME); ClusterImplConfig clusterImplConfig3 = (ClusterImplConfig) priorityChildConfig3.policySelection.getConfig(); - assertClusterImplConfig(clusterImplConfig3, CLUSTER1, EDS_SERVICE_NAME1, LRS_SERVER_NAME, 100L, + assertClusterImplConfig(clusterImplConfig3, CLUSTER1, EDS_SERVICE_NAME1, LRS_SERVER_INFO, 100L, tlsContext, Collections.emptyList(), WEIGHTED_TARGET_POLICY_NAME); WeightedTargetConfig weightedTargetConfig3 = (WeightedTargetConfig) clusterImplConfig3.childPolicy.getConfig(); @@ -549,7 +552,7 @@ public void onlyLogicalDnsCluster_endpointsResolved() { .isEqualTo(CLUSTER_IMPL_POLICY_NAME); ClusterImplConfig clusterImplConfig = (ClusterImplConfig) priorityChildConfig.policySelection.getConfig(); - assertClusterImplConfig(clusterImplConfig, CLUSTER_DNS, null, LRS_SERVER_NAME, 300L, null, + assertClusterImplConfig(clusterImplConfig, CLUSTER_DNS, null, LRS_SERVER_INFO, 300L, null, Collections.emptyList(), "pick_first"); assertAddressesEqual(Arrays.asList(endpoint1, endpoint2), childBalancer.addresses); } @@ -884,12 +887,12 @@ private static void assertPicker(SubchannelPicker picker, Status expectedStatus, } private static void assertClusterImplConfig(ClusterImplConfig config, String cluster, - @Nullable String edsServiceName, String lrsServerName, Long maxConcurrentRequests, + @Nullable String edsServiceName, ServerInfo lrsServerInfo, Long maxConcurrentRequests, @Nullable UpstreamTlsContext tlsContext, List dropCategories, String childPolicy) { assertThat(config.cluster).isEqualTo(cluster); assertThat(config.edsServiceName).isEqualTo(edsServiceName); - assertThat(config.lrsServerName).isEqualTo(lrsServerName); + assertThat(config.lrsServerInfo).isEqualTo(lrsServerInfo); assertThat(config.maxConcurrentRequests).isEqualTo(maxConcurrentRequests); assertThat(config.tlsContext).isEqualTo(tlsContext); assertThat(config.dropCategories).isEqualTo(dropCategories); From b3579db57480a49fcfd714505bbdb2faefacfee3 Mon Sep 17 00:00:00 2001 From: ZHANG Dapeng Date: Wed, 10 Nov 2021 08:38:44 -0800 Subject: [PATCH 32/50] xds: Migrate away deprecated fields in CsdsService (#8675) Migrate deprecate `xds_config` field to `generic_xds_configs` https://www.envoyproxy.io/docs/envoy/latest/api-v3/service/status/v3/csds.proto#service-status-v3-clientconfig As per grpc/proposal#267. The c++ version is grpc/grpc#27794 --- .../java/io/grpc/xds/AbstractXdsClient.java | 3 +- .../java/io/grpc/xds/ClientXdsClient.java | 9 - .../main/java/io/grpc/xds/CsdsService.java | 154 +---- xds/src/main/java/io/grpc/xds/XdsClient.java | 8 - .../java/io/grpc/xds/CsdsServiceTest.java | 557 ++---------------- 5 files changed, 68 insertions(+), 663 deletions(-) diff --git a/xds/src/main/java/io/grpc/xds/AbstractXdsClient.java b/xds/src/main/java/io/grpc/xds/AbstractXdsClient.java index e29949eb8e5..61780c60a55 100644 --- a/xds/src/main/java/io/grpc/xds/AbstractXdsClient.java +++ b/xds/src/main/java/io/grpc/xds/AbstractXdsClient.java @@ -333,7 +333,8 @@ String typeUrlV2() { } } - private static ResourceType fromTypeUrl(String typeUrl) { + @VisibleForTesting + static ResourceType fromTypeUrl(String typeUrl) { switch (typeUrl) { case ADS_TYPE_URL_LDS: // fall trough diff --git a/xds/src/main/java/io/grpc/xds/ClientXdsClient.java b/xds/src/main/java/io/grpc/xds/ClientXdsClient.java index c8172a87d18..4d45dc21d9e 100644 --- a/xds/src/main/java/io/grpc/xds/ClientXdsClient.java +++ b/xds/src/main/java/io/grpc/xds/ClientXdsClient.java @@ -2132,15 +2132,6 @@ public void run() { Bootstrapper.BootstrapInfo getBootstrapInfo() { return bootstrapInfo; } - - // TODO(https://github.com/grpc/grpc-java/issues/8629): remove this - @Override - String getCurrentVersion(ResourceType type) { - if (serverChannelMap.isEmpty()) { - return ""; - } - return serverChannelMap.values().iterator().next().getCurrentVersion(type); - } @Override public String toString() { diff --git a/xds/src/main/java/io/grpc/xds/CsdsService.java b/xds/src/main/java/io/grpc/xds/CsdsService.java index 5146930859d..89147536a6d 100644 --- a/xds/src/main/java/io/grpc/xds/CsdsService.java +++ b/xds/src/main/java/io/grpc/xds/CsdsService.java @@ -21,20 +21,11 @@ import com.google.common.annotations.VisibleForTesting; import com.google.protobuf.util.Timestamps; import io.envoyproxy.envoy.admin.v3.ClientResourceStatus; -import io.envoyproxy.envoy.admin.v3.ClustersConfigDump; -import io.envoyproxy.envoy.admin.v3.ClustersConfigDump.DynamicCluster; -import io.envoyproxy.envoy.admin.v3.EndpointsConfigDump; -import io.envoyproxy.envoy.admin.v3.EndpointsConfigDump.DynamicEndpointConfig; -import io.envoyproxy.envoy.admin.v3.ListenersConfigDump; -import io.envoyproxy.envoy.admin.v3.ListenersConfigDump.DynamicListener; -import io.envoyproxy.envoy.admin.v3.ListenersConfigDump.DynamicListenerState; -import io.envoyproxy.envoy.admin.v3.RoutesConfigDump; -import io.envoyproxy.envoy.admin.v3.RoutesConfigDump.DynamicRouteConfig; import io.envoyproxy.envoy.service.status.v3.ClientConfig; +import io.envoyproxy.envoy.service.status.v3.ClientConfig.GenericXdsConfig; import io.envoyproxy.envoy.service.status.v3.ClientStatusDiscoveryServiceGrpc; import io.envoyproxy.envoy.service.status.v3.ClientStatusRequest; import io.envoyproxy.envoy.service.status.v3.ClientStatusResponse; -import io.envoyproxy.envoy.service.status.v3.PerXdsConfig; import io.grpc.ExperimentalApi; import io.grpc.Status; import io.grpc.StatusException; @@ -150,124 +141,33 @@ private ClientStatusResponse getConfigDumpForRequest(ClientStatusRequest request @VisibleForTesting static ClientConfig getClientConfigForXdsClient(XdsClient xdsClient) { - ListenersConfigDump ldsConfig = dumpLdsConfig( - xdsClient.getSubscribedResourcesMetadata(ResourceType.LDS), - xdsClient.getCurrentVersion(ResourceType.LDS)); - RoutesConfigDump rdsConfig = dumpRdsConfig( - xdsClient.getSubscribedResourcesMetadata(ResourceType.RDS)); - ClustersConfigDump cdsConfig = dumpCdsConfig( - xdsClient.getSubscribedResourcesMetadata(ResourceType.CDS), - xdsClient.getCurrentVersion(ResourceType.CDS)); - EndpointsConfigDump edsConfig = dumpEdsConfig( - xdsClient.getSubscribedResourcesMetadata(ResourceType.EDS)); - - return ClientConfig.newBuilder() - .setNode(xdsClient.getBootstrapInfo().node().toEnvoyProtoNode()) - .addXdsConfig(PerXdsConfig.newBuilder().setListenerConfig(ldsConfig)) - .addXdsConfig(PerXdsConfig.newBuilder().setRouteConfig(rdsConfig)) - .addXdsConfig(PerXdsConfig.newBuilder().setClusterConfig(cdsConfig)) - .addXdsConfig(PerXdsConfig.newBuilder().setEndpointConfig(edsConfig)) - .build(); - } - - @VisibleForTesting - static ListenersConfigDump dumpLdsConfig( - Map resourcesMetadata, String version) { - ListenersConfigDump.Builder ldsConfig = ListenersConfigDump.newBuilder(); - for (Map.Entry entry : resourcesMetadata.entrySet()) { - ldsConfig.addDynamicListeners(buildDynamicListener(entry.getKey(), entry.getValue())); - } - return ldsConfig.setVersionInfo(version).build(); - } - - @VisibleForTesting - static DynamicListener buildDynamicListener(String name, ResourceMetadata metadata) { - DynamicListener.Builder dynamicListener = DynamicListener.newBuilder() - .setName(name) - .setClientStatus(metadataStatusToClientStatus(metadata.getStatus())); - if (metadata.getErrorState() != null) { - dynamicListener.setErrorState(metadataUpdateFailureStateToProto(metadata.getErrorState())); - } - DynamicListenerState.Builder dynamicListenerState = DynamicListenerState.newBuilder() - .setVersionInfo(metadata.getVersion()) - .setLastUpdated(Timestamps.fromNanos(metadata.getUpdateTimeNanos())); - if (metadata.getRawResource() != null) { - dynamicListenerState.setListener(metadata.getRawResource()); - } - return dynamicListener.setActiveState(dynamicListenerState).build(); - } - - @VisibleForTesting - static RoutesConfigDump dumpRdsConfig(Map resourcesMetadata) { - RoutesConfigDump.Builder rdsConfig = RoutesConfigDump.newBuilder(); - for (ResourceMetadata metadata : resourcesMetadata.values()) { - rdsConfig.addDynamicRouteConfigs(buildDynamicRouteConfig(metadata)); - } - return rdsConfig.build(); - } - - @VisibleForTesting - static DynamicRouteConfig buildDynamicRouteConfig(ResourceMetadata metadata) { - DynamicRouteConfig.Builder dynamicRouteConfig = DynamicRouteConfig.newBuilder() - .setVersionInfo(metadata.getVersion()) - .setClientStatus(metadataStatusToClientStatus(metadata.getStatus())) - .setLastUpdated(Timestamps.fromNanos(metadata.getUpdateTimeNanos())); - if (metadata.getErrorState() != null) { - dynamicRouteConfig.setErrorState(metadataUpdateFailureStateToProto(metadata.getErrorState())); - } - if (metadata.getRawResource() != null) { - dynamicRouteConfig.setRouteConfig(metadata.getRawResource()); - } - return dynamicRouteConfig.build(); - } - - @VisibleForTesting - static ClustersConfigDump dumpCdsConfig( - Map resourcesMetadata, String version) { - ClustersConfigDump.Builder cdsConfig = ClustersConfigDump.newBuilder(); - for (ResourceMetadata metadata : resourcesMetadata.values()) { - cdsConfig.addDynamicActiveClusters(buildDynamicCluster(metadata)); - } - return cdsConfig.setVersionInfo(version).build(); - } - - @VisibleForTesting - static DynamicCluster buildDynamicCluster(ResourceMetadata metadata) { - DynamicCluster.Builder dynamicCluster = DynamicCluster.newBuilder() - .setVersionInfo(metadata.getVersion()) - .setClientStatus(metadataStatusToClientStatus(metadata.getStatus())) - .setLastUpdated(Timestamps.fromNanos(metadata.getUpdateTimeNanos())); - if (metadata.getErrorState() != null) { - dynamicCluster.setErrorState(metadataUpdateFailureStateToProto(metadata.getErrorState())); - } - if (metadata.getRawResource() != null) { - dynamicCluster.setCluster(metadata.getRawResource()); - } - return dynamicCluster.build(); - } - - @VisibleForTesting - static EndpointsConfigDump dumpEdsConfig(Map resourcesMetadata) { - EndpointsConfigDump.Builder edsConfig = EndpointsConfigDump.newBuilder(); - for (ResourceMetadata metadata : resourcesMetadata.values()) { - edsConfig.addDynamicEndpointConfigs(buildDynamicEndpointConfig(metadata)); - } - return edsConfig.build(); - } - - @VisibleForTesting - static DynamicEndpointConfig buildDynamicEndpointConfig(ResourceMetadata metadata) { - DynamicEndpointConfig.Builder dynamicRouteConfig = DynamicEndpointConfig.newBuilder() - .setVersionInfo(metadata.getVersion()) - .setClientStatus(metadataStatusToClientStatus(metadata.getStatus())) - .setLastUpdated(Timestamps.fromNanos(metadata.getUpdateTimeNanos())); - if (metadata.getErrorState() != null) { - dynamicRouteConfig.setErrorState(metadataUpdateFailureStateToProto(metadata.getErrorState())); - } - if (metadata.getRawResource() != null) { - dynamicRouteConfig.setEndpointConfig(metadata.getRawResource()); + ClientConfig.Builder builder = ClientConfig.newBuilder() + .setNode(xdsClient.getBootstrapInfo().node().toEnvoyProtoNode()); + for (ResourceType type : ResourceType.values()) { + if (type == ResourceType.UNKNOWN) { + continue; + } + Map metadataMap = xdsClient.getSubscribedResourcesMetadata(type); + for (String resourceName : metadataMap.keySet()) { + ResourceMetadata metadata = metadataMap.get(resourceName); + GenericXdsConfig.Builder genericXdsConfigBuilder = GenericXdsConfig.newBuilder() + .setTypeUrl(type.typeUrl()) + .setName(resourceName) + .setClientStatus(metadataStatusToClientStatus(metadata.getStatus())); + if (metadata.getRawResource() != null) { + genericXdsConfigBuilder + .setVersionInfo(metadata.getVersion()) + .setLastUpdated(Timestamps.fromNanos(metadata.getUpdateTimeNanos())) + .setXdsConfig(metadata.getRawResource()); + } + if (metadata.getStatus() == ResourceMetadataStatus.NACKED) { + genericXdsConfigBuilder + .setErrorState(metadataUpdateFailureStateToProto(metadata.getErrorState())); + } + builder.addGenericXdsConfigs(genericXdsConfigBuilder); + } } - return dynamicRouteConfig.build(); + return builder.build(); } @VisibleForTesting diff --git a/xds/src/main/java/io/grpc/xds/XdsClient.java b/xds/src/main/java/io/grpc/xds/XdsClient.java index 9a6e28b3f5e..65d4d67c427 100644 --- a/xds/src/main/java/io/grpc/xds/XdsClient.java +++ b/xds/src/main/java/io/grpc/xds/XdsClient.java @@ -494,14 +494,6 @@ TlsContextManager getTlsContextManager() { throw new UnsupportedOperationException(); } - /** - * Returns the latest accepted version of the given resource type. - */ - // TODO(https://github.com/grpc/grpc-java/issues/8629): remove this - String getCurrentVersion(ResourceType type) { - throw new UnsupportedOperationException(); - } - Map getSubscribedResourcesMetadata(ResourceType type) { throw new UnsupportedOperationException(); } diff --git a/xds/src/test/java/io/grpc/xds/CsdsServiceTest.java b/xds/src/test/java/io/grpc/xds/CsdsServiceTest.java index 9a50e2e0599..7c6abee2835 100644 --- a/xds/src/test/java/io/grpc/xds/CsdsServiceTest.java +++ b/xds/src/test/java/io/grpc/xds/CsdsServiceTest.java @@ -17,7 +17,6 @@ package io.grpc.xds; import static com.google.common.truth.Truth.assertThat; -import static com.google.common.truth.Truth.assertWithMessage; import static io.grpc.xds.AbstractXdsClient.ResourceType.CDS; import static io.grpc.xds.AbstractXdsClient.ResourceType.EDS; import static io.grpc.xds.AbstractXdsClient.ResourceType.LDS; @@ -26,28 +25,17 @@ import com.google.common.collect.ImmutableMap; import com.google.protobuf.Any; -import com.google.protobuf.Timestamp; import io.envoyproxy.envoy.admin.v3.ClientResourceStatus; -import io.envoyproxy.envoy.admin.v3.ClustersConfigDump; -import io.envoyproxy.envoy.admin.v3.ClustersConfigDump.DynamicCluster; -import io.envoyproxy.envoy.admin.v3.EndpointsConfigDump; -import io.envoyproxy.envoy.admin.v3.EndpointsConfigDump.DynamicEndpointConfig; -import io.envoyproxy.envoy.admin.v3.ListenersConfigDump; -import io.envoyproxy.envoy.admin.v3.ListenersConfigDump.DynamicListener; -import io.envoyproxy.envoy.admin.v3.ListenersConfigDump.DynamicListenerState; -import io.envoyproxy.envoy.admin.v3.RoutesConfigDump; -import io.envoyproxy.envoy.admin.v3.RoutesConfigDump.DynamicRouteConfig; -import io.envoyproxy.envoy.admin.v3.UpdateFailureState; import io.envoyproxy.envoy.config.cluster.v3.Cluster; import io.envoyproxy.envoy.config.core.v3.Node; import io.envoyproxy.envoy.config.endpoint.v3.ClusterLoadAssignment; import io.envoyproxy.envoy.config.listener.v3.Listener; import io.envoyproxy.envoy.config.route.v3.RouteConfiguration; import io.envoyproxy.envoy.service.status.v3.ClientConfig; +import io.envoyproxy.envoy.service.status.v3.ClientConfig.GenericXdsConfig; import io.envoyproxy.envoy.service.status.v3.ClientStatusDiscoveryServiceGrpc; import io.envoyproxy.envoy.service.status.v3.ClientStatusRequest; import io.envoyproxy.envoy.service.status.v3.ClientStatusResponse; -import io.envoyproxy.envoy.service.status.v3.PerXdsConfig; import io.envoyproxy.envoy.type.matcher.v3.NodeMatcher; import io.grpc.InsecureChannelCredentials; import io.grpc.Status; @@ -92,11 +80,6 @@ Bootstrapper.BootstrapInfo getBootstrapInfo() { .build(); } - @Override - String getCurrentVersion(ResourceType type) { - return "getCurrentVersion." + type.name(); - } - @Override Map getSubscribedResourcesMetadata(ResourceType type) { return ImmutableMap.of(); @@ -266,23 +249,11 @@ public static class MetadataToProtoTests { private static final String RDS_RESOURCE = "route-configuration.googleapis.com"; private static final String CDS_RESOURCE = "cluster.googleapis.com"; private static final String EDS_RESOURCE = "cluster-load-assignment.googleapis.com"; - private static final String VERSION_ACK = "42"; - private static final String VERSION_NACK = "43"; - private static final String ERROR = "Parse error line 1\n Parse error line 2"; - - // Test timestamps. - private static final Timestamp TIMESTAMP_ZERO = Timestamp.getDefaultInstance(); + private static final String VERSION_ACK_LDS = "42"; + private static final String VERSION_ACK_RDS = "38"; + private static final String VERSION_ACK_CDS = "51"; + private static final String VERSION_ACK_EDS = "29"; private static final long NANOS_LAST_UPDATE = 1577923199_606042047L; - private static final Timestamp TIMESTAMP_LAST_UPDATE = Timestamp.newBuilder() - .setSeconds(1577923199L) // 2020-01-01T23:59:59Z - .setNanos(606042047) - .build(); - private static final long NANOS_FAILED_UPDATE = 1609545599_732105843L; - private static final Timestamp TIMESTAMP_FAILED_UPDATE = Timestamp.newBuilder() - .setSeconds(1609545599L) // 2021-01-01T23:59:59Z - .setNanos(732105843) - .build(); - // Raw resources. private static final Any RAW_LISTENER = Any.pack(Listener.newBuilder().setName(LDS_RESOURCE).build()); @@ -293,388 +264,15 @@ public static class MetadataToProtoTests { private static final Any RAW_CLUSTER_LOAD_ASSIGNMENT = Any.pack(ClusterLoadAssignment.newBuilder().setClusterName(EDS_RESOURCE).build()); - // Test metadata: no data received states. - private static final ResourceMetadata METADATA_UNKNOWN = - ResourceMetadata.newResourceMetadataUnknown(); - private static final ResourceMetadata METADATA_DOES_NOT_EXIST = - ResourceMetadata.newResourceMetadataDoesNotExist(); - private static final ResourceMetadata METADATA_REQUESTED = - ResourceMetadata.newResourceMetadataRequested(); - // Test metadata: resource acknowledged state, per resource type. private static final ResourceMetadata METADATA_ACKED_LDS = ResourceMetadata - .newResourceMetadataAcked(RAW_LISTENER, VERSION_ACK, NANOS_LAST_UPDATE); + .newResourceMetadataAcked(RAW_LISTENER, VERSION_ACK_LDS, NANOS_LAST_UPDATE); private static final ResourceMetadata METADATA_ACKED_RDS = ResourceMetadata - .newResourceMetadataAcked(RAW_ROUTE_CONFIGURATION, VERSION_ACK, NANOS_LAST_UPDATE); + .newResourceMetadataAcked(RAW_ROUTE_CONFIGURATION, VERSION_ACK_RDS, NANOS_LAST_UPDATE); private static final ResourceMetadata METADATA_ACKED_CDS = ResourceMetadata - .newResourceMetadataAcked(RAW_CLUSTER, VERSION_ACK, NANOS_LAST_UPDATE); + .newResourceMetadataAcked(RAW_CLUSTER, VERSION_ACK_CDS, NANOS_LAST_UPDATE); private static final ResourceMetadata METADATA_ACKED_EDS = ResourceMetadata - .newResourceMetadataAcked(RAW_CLUSTER_LOAD_ASSIGNMENT, VERSION_ACK, NANOS_LAST_UPDATE); - - // Test resources list. - private static final ImmutableMap RESOURCES_METADATA = - ImmutableMap.of("A", METADATA_UNKNOWN, "B", METADATA_REQUESTED); - - /* LDS tests */ - - @Test - public void dumpLdsConfig() { - ListenersConfigDump ldsConfig = CsdsService.dumpLdsConfig(RESOURCES_METADATA, VERSION_ACK); - assertThat(ldsConfig.getVersionInfo()).isEqualTo(VERSION_ACK); - assertThat(ldsConfig.getStaticListenersCount()).isEqualTo(0); - assertThat(ldsConfig.getDynamicListenersCount()).isEqualTo(2); - // Minimal check to confirm that resources generated from corresponding metadata. - DynamicListener listenerA = ldsConfig.getDynamicListeners(0); - assertThat(listenerA.getName()).isEqualTo("A"); - assertThat(listenerA.getClientStatus()).isEqualTo(ClientResourceStatus.UNKNOWN); - DynamicListener listenerB = ldsConfig.getDynamicListeners(1); - assertThat(listenerB.getName()).isEqualTo("B"); - assertThat(listenerB.getClientStatus()).isEqualTo(ClientResourceStatus.REQUESTED); - } - - @Test - public void buildDynamicListener_metadataUnknown() { - DynamicListener dynamicListener = - CsdsService.buildDynamicListener(LDS_RESOURCE, METADATA_UNKNOWN); - verifyDynamicListener(dynamicListener, ClientResourceStatus.UNKNOWN); - verifyDynamicListenerStateNoData(dynamicListener.getActiveState()); - } - - @Test - public void buildDynamicListener_metadataDoesNotExist() { - DynamicListener dynamicListener = - CsdsService.buildDynamicListener(LDS_RESOURCE, METADATA_DOES_NOT_EXIST); - verifyDynamicListener(dynamicListener, ClientResourceStatus.DOES_NOT_EXIST); - verifyDynamicListenerStateNoData(dynamicListener.getActiveState()); - } - - @Test - public void buildDynamicListener_metadataRequested() { - DynamicListener dynamicListener = - CsdsService.buildDynamicListener(LDS_RESOURCE, METADATA_REQUESTED); - verifyDynamicListener(dynamicListener, ClientResourceStatus.REQUESTED); - verifyDynamicListenerStateNoData(dynamicListener.getActiveState()); - } - - @Test - public void buildDynamicListener_metadataAcked() { - DynamicListener dynamicListener = - CsdsService.buildDynamicListener(LDS_RESOURCE, METADATA_ACKED_LDS); - verifyDynamicListener(dynamicListener, ClientResourceStatus.ACKED); - verifyDynamicListenerStateAccepted(dynamicListener.getActiveState()); - } - - @Test - public void buildDynamicListener_metadataNackedFromRequested() { - ResourceMetadata metadata = ResourceMetadata.newResourceMetadataNacked( - METADATA_REQUESTED, VERSION_NACK, NANOS_FAILED_UPDATE, ERROR); - DynamicListener dynamicListener = CsdsService.buildDynamicListener(LDS_RESOURCE, metadata); - verifyDynamicListener(dynamicListener, ClientResourceStatus.NACKED); - verifyErrorState(dynamicListener.getErrorState()); - verifyDynamicListenerStateNoData(dynamicListener.getActiveState()); - } - - @Test - public void buildDynamicListener_metadataNackedFromAcked() { - ResourceMetadata metadata = ResourceMetadata.newResourceMetadataNacked( - METADATA_ACKED_LDS, VERSION_NACK, NANOS_FAILED_UPDATE, ERROR); - DynamicListener dynamicListener = CsdsService.buildDynamicListener(LDS_RESOURCE, metadata); - verifyDynamicListener(dynamicListener, ClientResourceStatus.NACKED); - verifyErrorState(dynamicListener.getErrorState()); - verifyDynamicListenerStateAccepted(dynamicListener.getActiveState()); - } - - private void verifyDynamicListener( - DynamicListener dynamicListener, ClientResourceStatus status) { - assertWithMessage("name").that(dynamicListener.getName()).isEqualTo(LDS_RESOURCE); - assertWithMessage("active_state").that(dynamicListener.hasActiveState()).isTrue(); - assertWithMessage("warming_state").that(dynamicListener.hasWarmingState()).isFalse(); - assertWithMessage("draining_state").that(dynamicListener.hasDrainingState()).isFalse(); - assertWithMessage("error_state").that(dynamicListener.hasErrorState()) - .isEqualTo(status.equals(ClientResourceStatus.NACKED)); - assertWithMessage("client_status").that(dynamicListener.getClientStatus()).isEqualTo(status); - } - - private void verifyDynamicListenerStateNoData(DynamicListenerState dynamicListenerState) { - assertWithMessage("version_info").that(dynamicListenerState.getVersionInfo()).isEmpty(); - assertWithMessage("listener").that(dynamicListenerState.hasListener()).isFalse(); - assertWithMessage("last_updated").that(dynamicListenerState.getLastUpdated()) - .isEqualTo(TIMESTAMP_ZERO); - } - - private void verifyDynamicListenerStateAccepted(DynamicListenerState dynamicListenerState) { - assertWithMessage("version_info").that(dynamicListenerState.getVersionInfo()) - .isEqualTo(VERSION_ACK); - assertWithMessage("listener").that(dynamicListenerState.hasListener()).isTrue(); - assertWithMessage("listener").that(dynamicListenerState.getListener()) - .isEqualTo(RAW_LISTENER); - assertWithMessage("last_updated").that(dynamicListenerState.getLastUpdated()) - .isEqualTo(TIMESTAMP_LAST_UPDATE); - } - - /* RDS tests */ - - @Test - public void dumpRdsConfig() { - RoutesConfigDump rdsConfig = CsdsService.dumpRdsConfig(RESOURCES_METADATA); - assertThat(rdsConfig.getStaticRouteConfigsCount()).isEqualTo(0); - assertThat(rdsConfig.getDynamicRouteConfigsCount()).isEqualTo(2); - // Minimal check to confirm that resources generated from corresponding metadata. - assertThat(rdsConfig.getDynamicRouteConfigs(0).getClientStatus()) - .isEqualTo(ClientResourceStatus.UNKNOWN); - assertThat(rdsConfig.getDynamicRouteConfigs(1).getClientStatus()) - .isEqualTo(ClientResourceStatus.REQUESTED); - } - - @Test - public void buildDynamicRouteConfig_metadataUnknown() { - verifyDynamicRouteConfigNoData( - CsdsService.buildDynamicRouteConfig(METADATA_UNKNOWN), - ClientResourceStatus.UNKNOWN); - } - - @Test - public void buildDynamicRouteConfig_metadataDoesNotExist() { - verifyDynamicRouteConfigNoData( - CsdsService.buildDynamicRouteConfig(METADATA_DOES_NOT_EXIST), - ClientResourceStatus.DOES_NOT_EXIST); - } - - @Test - public void buildDynamicRouteConfig_metadataRequested() { - verifyDynamicRouteConfigNoData( - CsdsService.buildDynamicRouteConfig(METADATA_REQUESTED), - ClientResourceStatus.REQUESTED); - } - - @Test - public void buildDynamicRouteConfig_metadataAcked() { - verifyDynamicRouteConfigAccepted( - CsdsService.buildDynamicRouteConfig(METADATA_ACKED_RDS), - ClientResourceStatus.ACKED); - } - - @Test - public void buildDynamicRouteConfig_metadataNackedFromRequested() { - ResourceMetadata metadata = ResourceMetadata.newResourceMetadataNacked( - METADATA_REQUESTED, VERSION_NACK, NANOS_FAILED_UPDATE, ERROR); - DynamicRouteConfig dynamicRouteConfig = CsdsService.buildDynamicRouteConfig(metadata); - verifyDynamicRouteConfigNoData(dynamicRouteConfig, ClientResourceStatus.NACKED); - verifyErrorState(dynamicRouteConfig.getErrorState()); - } - - @Test - public void buildDynamicRouteConfig_metadataNackedFromAcked() { - ResourceMetadata metadata = ResourceMetadata.newResourceMetadataNacked( - METADATA_ACKED_RDS, VERSION_NACK, NANOS_FAILED_UPDATE, ERROR); - DynamicRouteConfig dynamicRouteConfig = CsdsService.buildDynamicRouteConfig(metadata); - verifyDynamicRouteConfigAccepted(dynamicRouteConfig, ClientResourceStatus.NACKED); - verifyErrorState(dynamicRouteConfig.getErrorState()); - } - - private void verifyDynamicRouteConfigNoData( - DynamicRouteConfig dynamicRouteConfig, ClientResourceStatus status) { - assertWithMessage("version_info").that(dynamicRouteConfig.getVersionInfo()).isEmpty(); - assertWithMessage("route_config").that(dynamicRouteConfig.hasRouteConfig()).isFalse(); - assertWithMessage("last_updated").that(dynamicRouteConfig.getLastUpdated()) - .isEqualTo(TIMESTAMP_ZERO); - assertWithMessage("error_state").that(dynamicRouteConfig.hasErrorState()) - .isEqualTo(status.equals(ClientResourceStatus.NACKED)); - assertWithMessage("client_status").that(dynamicRouteConfig.getClientStatus()) - .isEqualTo(status); - } - - private void verifyDynamicRouteConfigAccepted( - DynamicRouteConfig dynamicRouteConfig, ClientResourceStatus status) { - assertWithMessage("version_info").that(dynamicRouteConfig.getVersionInfo()) - .isEqualTo(VERSION_ACK); - assertWithMessage("route_config").that(dynamicRouteConfig.hasRouteConfig()).isTrue(); - assertWithMessage("route_config").that(dynamicRouteConfig.getRouteConfig()) - .isEqualTo(RAW_ROUTE_CONFIGURATION); - assertWithMessage("last_updated").that(dynamicRouteConfig.getLastUpdated()) - .isEqualTo(TIMESTAMP_LAST_UPDATE); - assertWithMessage("error_state").that(dynamicRouteConfig.hasErrorState()) - .isEqualTo(status.equals(ClientResourceStatus.NACKED)); - assertWithMessage("client_status").that(dynamicRouteConfig.getClientStatus()) - .isEqualTo(status); - } - - /* CDS tests */ - - @Test - public void dumpCdsConfig() { - ClustersConfigDump cdsConfig = CsdsService.dumpCdsConfig(RESOURCES_METADATA, VERSION_ACK); - assertThat(cdsConfig.getVersionInfo()).isEqualTo(VERSION_ACK); - assertThat(cdsConfig.getStaticClustersCount()).isEqualTo(0); - assertThat(cdsConfig.getDynamicWarmingClustersCount()).isEqualTo(0); - assertThat(cdsConfig.getDynamicActiveClustersCount()).isEqualTo(2); - // Minimal check to confirm that resources generated from corresponding metadata. - assertThat(cdsConfig.getDynamicActiveClusters(0).getClientStatus()) - .isEqualTo(ClientResourceStatus.UNKNOWN); - assertThat(cdsConfig.getDynamicActiveClusters(1).getClientStatus()) - .isEqualTo(ClientResourceStatus.REQUESTED); - } - - @Test - public void buildDynamicCluster_metadataUnknown() { - verifyDynamicClusterNoData( - CsdsService.buildDynamicCluster(METADATA_UNKNOWN), - ClientResourceStatus.UNKNOWN); - } - - @Test - public void buildDynamicCluster_metadataDoesNotExist() { - verifyDynamicClusterNoData( - CsdsService.buildDynamicCluster(METADATA_DOES_NOT_EXIST), - ClientResourceStatus.DOES_NOT_EXIST); - } - - @Test - public void buildDynamicCluster_metadataRequested() { - verifyDynamicClusterNoData( - CsdsService.buildDynamicCluster(METADATA_REQUESTED), - ClientResourceStatus.REQUESTED); - } - - @Test - public void buildDynamicCluster_metadataAcked() { - verifyDynamicClusterAccepted( - CsdsService.buildDynamicCluster(METADATA_ACKED_CDS), - ClientResourceStatus.ACKED); - } - - @Test - public void buildDynamicCluster_metadataNackedFromRequested() { - ResourceMetadata metadata = ResourceMetadata.newResourceMetadataNacked( - METADATA_REQUESTED, VERSION_NACK, NANOS_FAILED_UPDATE, ERROR); - DynamicCluster dynamicCluster = CsdsService.buildDynamicCluster(metadata); - verifyDynamicClusterNoData(dynamicCluster, ClientResourceStatus.NACKED); - verifyErrorState(dynamicCluster.getErrorState()); - } - - @Test - public void buildDynamicCluster_metadataNackedFromAcked() { - ResourceMetadata metadata = ResourceMetadata.newResourceMetadataNacked( - METADATA_ACKED_CDS, VERSION_NACK, NANOS_FAILED_UPDATE, ERROR); - DynamicCluster dynamicCluster = CsdsService.buildDynamicCluster(metadata); - verifyDynamicClusterAccepted(dynamicCluster, ClientResourceStatus.NACKED); - verifyErrorState(dynamicCluster.getErrorState()); - } - - private void verifyDynamicClusterNoData( - DynamicCluster dynamicCluster, ClientResourceStatus status) { - assertWithMessage("version_info").that(dynamicCluster.getVersionInfo()).isEmpty(); - assertWithMessage("route_config").that(dynamicCluster.hasCluster()).isFalse(); - assertWithMessage("last_updated").that(dynamicCluster.getLastUpdated()) - .isEqualTo(TIMESTAMP_ZERO); - assertWithMessage("error_state").that(dynamicCluster.hasErrorState()) - .isEqualTo(status.equals(ClientResourceStatus.NACKED)); - assertWithMessage("client_status").that(dynamicCluster.getClientStatus()).isEqualTo(status); - } - - private void verifyDynamicClusterAccepted( - DynamicCluster dynamicCluster, ClientResourceStatus status) { - assertWithMessage("version_info").that(dynamicCluster.getVersionInfo()) - .isEqualTo(VERSION_ACK); - assertWithMessage("route_config").that(dynamicCluster.hasCluster()).isTrue(); - assertWithMessage("route_config").that(dynamicCluster.getCluster()).isEqualTo(RAW_CLUSTER); - assertWithMessage("last_updated").that(dynamicCluster.getLastUpdated()) - .isEqualTo(TIMESTAMP_LAST_UPDATE); - assertWithMessage("error_state").that(dynamicCluster.hasErrorState()) - .isEqualTo(status.equals(ClientResourceStatus.NACKED)); - assertWithMessage("client_status").that(dynamicCluster.getClientStatus()).isEqualTo(status); - } - - /* EDS tests */ - - @Test - public void dumpEdsConfig() { - EndpointsConfigDump edsConfig = CsdsService.dumpEdsConfig(RESOURCES_METADATA); - assertThat(edsConfig.getStaticEndpointConfigsCount()).isEqualTo(0); - assertThat(edsConfig.getDynamicEndpointConfigsCount()).isEqualTo(2); - // Minimal check to confirm that resources generated from corresponding metadata. - assertThat(edsConfig.getDynamicEndpointConfigs(0).getClientStatus()) - .isEqualTo(ClientResourceStatus.UNKNOWN); - assertThat(edsConfig.getDynamicEndpointConfigs(1).getClientStatus()) - .isEqualTo(ClientResourceStatus.REQUESTED); - } - - @Test - public void buildDynamicEndpointConfig_metadataUnknown() { - buildDynamicEndpointConfigNoData( - CsdsService.buildDynamicEndpointConfig(METADATA_UNKNOWN), - ClientResourceStatus.UNKNOWN); - } - - @Test - public void buildDynamicEndpointConfig_metadataDoesNotExist() { - buildDynamicEndpointConfigNoData( - CsdsService.buildDynamicEndpointConfig(METADATA_DOES_NOT_EXIST), - ClientResourceStatus.DOES_NOT_EXIST); - } - - @Test - public void buildDynamicEndpointConfig_metadataRequested() { - buildDynamicEndpointConfigNoData( - CsdsService.buildDynamicEndpointConfig(METADATA_REQUESTED), - ClientResourceStatus.REQUESTED); - } - - @Test - public void buildDynamicEndpointConfig_metadataAcked() { - verifyDynamicEndpointConfigAccepted( - CsdsService.buildDynamicEndpointConfig(METADATA_ACKED_EDS), - ClientResourceStatus.ACKED); - } - - @Test - public void buildDynamicEndpointConfig_metadataNackedFromRequested() { - ResourceMetadata metadata = ResourceMetadata.newResourceMetadataNacked( - METADATA_REQUESTED, VERSION_NACK, NANOS_FAILED_UPDATE, ERROR); - DynamicEndpointConfig dynamicEndpointConfig = - CsdsService.buildDynamicEndpointConfig(metadata); - buildDynamicEndpointConfigNoData(dynamicEndpointConfig, ClientResourceStatus.NACKED); - verifyErrorState(dynamicEndpointConfig.getErrorState()); - } - - @Test - public void buildDynamicEndpointConfig_metadataNackedFromAcked() { - ResourceMetadata metadata = ResourceMetadata.newResourceMetadataNacked( - METADATA_ACKED_EDS, VERSION_NACK, NANOS_FAILED_UPDATE, ERROR); - DynamicEndpointConfig dynamicEndpointConfig = - CsdsService.buildDynamicEndpointConfig(metadata); - verifyDynamicEndpointConfigAccepted(dynamicEndpointConfig, ClientResourceStatus.NACKED); - verifyErrorState(dynamicEndpointConfig.getErrorState()); - } - - private void buildDynamicEndpointConfigNoData( - DynamicEndpointConfig dynamicEndpointConfig, ClientResourceStatus status) { - assertWithMessage("version_info").that(dynamicEndpointConfig.getVersionInfo()).isEmpty(); - assertWithMessage("route_config").that(dynamicEndpointConfig.hasEndpointConfig()).isFalse(); - assertWithMessage("last_updated").that(dynamicEndpointConfig.getLastUpdated()) - .isEqualTo(TIMESTAMP_ZERO); - assertWithMessage("error_state").that(dynamicEndpointConfig.hasErrorState()) - .isEqualTo(status.equals(ClientResourceStatus.NACKED)); - assertWithMessage("client_status").that(dynamicEndpointConfig.getClientStatus()) - .isEqualTo(status); - } - - private void verifyDynamicEndpointConfigAccepted( - DynamicEndpointConfig dynamicEndpointConfig, ClientResourceStatus status) { - assertWithMessage("version_info").that(dynamicEndpointConfig.getVersionInfo()) - .isEqualTo(VERSION_ACK); - assertWithMessage("route_config").that(dynamicEndpointConfig.hasEndpointConfig()).isTrue(); - assertWithMessage("route_config").that(dynamicEndpointConfig.getEndpointConfig()) - .isEqualTo(RAW_CLUSTER_LOAD_ASSIGNMENT); - assertWithMessage("last_updated").that(dynamicEndpointConfig.getLastUpdated()) - .isEqualTo(TIMESTAMP_LAST_UPDATE); - assertWithMessage("error_state").that(dynamicEndpointConfig.hasErrorState()) - .isEqualTo(status.equals(ClientResourceStatus.NACKED)); - assertWithMessage("client_status").that(dynamicEndpointConfig.getClientStatus()) - .isEqualTo(status); - } - - /* Common methods. */ + .newResourceMetadataAcked(RAW_CLUSTER_LOAD_ASSIGNMENT, VERSION_ACK_EDS, NANOS_LAST_UPDATE); @Test public void metadataStatusToClientStatus() { @@ -691,7 +289,7 @@ public void metadataStatusToClientStatus() { } @Test - public void getClientConfigForXdsClient_subscribedResourcesToPerXdsConfig() { + public void getClientConfigForXdsClient_subscribedResourcesToGenericXdsConfig() { ClientConfig clientConfig = CsdsService.getClientConfigForXdsClient(new XdsClient() { @Override Bootstrapper.BootstrapInfo getBootstrapInfo() { @@ -703,11 +301,6 @@ Bootstrapper.BootstrapInfo getBootstrapInfo() { .build(); } - @Override - String getCurrentVersion(ResourceType type) { - return "getCurrentVersion." + type.name(); - } - @Override Map getSubscribedResourcesMetadata(ResourceType type) { switch (type) { @@ -730,58 +323,35 @@ Map getSubscribedResourcesMetadata(ResourceType type) // Minimal verification to confirm that the data/metadata XdsClient provides, // is propagated to the correct resource types. - @SuppressWarnings("deprecation") - int xdsConfigCount = clientConfig.getXdsConfigCount(); + int xdsConfigCount = clientConfig.getGenericXdsConfigsCount(); assertThat(xdsConfigCount).isEqualTo(4); - EnumMap configDumps = mapConfigDumps(clientConfig); + EnumMap configDumps = mapConfigDumps(clientConfig); assertThat(configDumps.keySet()).containsExactly(LDS, RDS, CDS, EDS); // LDS. - // Both the version provided by XdsClient.getCurrentVersion(), - // and the resource name provided by XdsClient.getSubscribedResourcesMetadata() are used. - PerXdsConfig perXdsConfigLds = configDumps.get(LDS); - verifyPerXdsConfigEmptyFields(perXdsConfigLds); - ListenersConfigDump listenerConfig = perXdsConfigLds.getListenerConfig(); - assertThat(listenerConfig.getVersionInfo()).isEqualTo("getCurrentVersion.LDS"); - assertThat(listenerConfig.getDynamicListenersCount()).isEqualTo(1); - DynamicListener dynamicListener = listenerConfig.getDynamicListeners(0); - assertThat(dynamicListener.getName()).isEqualTo("subscribedResourceName.LDS"); - assertThat(dynamicListener.getClientStatus()).isEqualTo(ClientResourceStatus.ACKED); - assertThat(dynamicListener.getActiveState().getVersionInfo()).isEqualTo(VERSION_ACK); + GenericXdsConfig genericXdsConfigLds = configDumps.get(LDS); + assertThat(genericXdsConfigLds.getName()).isEqualTo("subscribedResourceName.LDS"); + assertThat(genericXdsConfigLds.getClientStatus()).isEqualTo(ClientResourceStatus.ACKED); + assertThat(genericXdsConfigLds.getVersionInfo()).isEqualTo(VERSION_ACK_LDS); + assertThat(genericXdsConfigLds.getXdsConfig()).isEqualTo(RAW_LISTENER); // RDS. - // Neither the version provided by XdsClient.getCurrentVersion(), - // nor the resource name provided by XdsClient.getSubscribedResourcesMetadata() are used. - PerXdsConfig perXdsConfigRds = configDumps.get(RDS); - verifyPerXdsConfigEmptyFields(perXdsConfigRds); - RoutesConfigDump routeConfig = perXdsConfigRds.getRouteConfig(); - assertThat(routeConfig.getDynamicRouteConfigsCount()).isEqualTo(1); - DynamicRouteConfig dynamicRouteConfig = routeConfig.getDynamicRouteConfigs(0); - assertThat(dynamicRouteConfig.getClientStatus()).isEqualTo(ClientResourceStatus.ACKED); - assertThat(dynamicRouteConfig.getVersionInfo()).isEqualTo(VERSION_ACK); + GenericXdsConfig genericXdsConfigRds = configDumps.get(RDS); + assertThat(genericXdsConfigRds.getClientStatus()).isEqualTo(ClientResourceStatus.ACKED); + assertThat(genericXdsConfigRds.getVersionInfo()).isEqualTo(VERSION_ACK_RDS); + assertThat(genericXdsConfigRds.getXdsConfig()).isEqualTo(RAW_ROUTE_CONFIGURATION); // CDS. - // Only the version provided by XdsClient.getCurrentVersion() is used, - // the resource name provided by XdsClient.getSubscribedResourcesMetadata() is ignored. - PerXdsConfig perXdsConfigCds = configDumps.get(CDS); - verifyPerXdsConfigEmptyFields(perXdsConfigRds); - ClustersConfigDump clusterConfig = perXdsConfigCds.getClusterConfig(); - assertThat(clusterConfig.getVersionInfo()).isEqualTo("getCurrentVersion.CDS"); - assertThat(clusterConfig.getDynamicActiveClustersCount()).isEqualTo(1); - DynamicCluster dynamicCluster = clusterConfig.getDynamicActiveClusters(0); - assertThat(dynamicCluster.getClientStatus()).isEqualTo(ClientResourceStatus.ACKED); - assertThat(dynamicCluster.getVersionInfo()).isEqualTo(VERSION_ACK); + GenericXdsConfig genericXdsConfigCds = configDumps.get(CDS); + assertThat(genericXdsConfigCds.getClientStatus()).isEqualTo(ClientResourceStatus.ACKED); + assertThat(genericXdsConfigCds.getVersionInfo()).isEqualTo(VERSION_ACK_CDS); + assertThat(genericXdsConfigCds.getXdsConfig()).isEqualTo(RAW_CLUSTER); // RDS. - // Neither the version provided by XdsClient.getCurrentVersion(), - // nor the resource name provided by XdsClient.getSubscribedResourcesMetadata() are used. - PerXdsConfig perXdsConfigEds = configDumps.get(EDS); - verifyPerXdsConfigEmptyFields(perXdsConfigEds); - EndpointsConfigDump endpointConfig = perXdsConfigEds.getEndpointConfig(); - assertThat(endpointConfig.getDynamicEndpointConfigsCount()).isEqualTo(1); - DynamicEndpointConfig dynamicEndpointConfig = endpointConfig.getDynamicEndpointConfigs(0); - assertThat(dynamicEndpointConfig.getClientStatus()).isEqualTo(ClientResourceStatus.ACKED); - assertThat(dynamicEndpointConfig.getVersionInfo()).isEqualTo(VERSION_ACK); + GenericXdsConfig genericXdsConfigEds = configDumps.get(EDS); + assertThat(genericXdsConfigEds.getClientStatus()).isEqualTo(ClientResourceStatus.ACKED); + assertThat(genericXdsConfigEds.getVersionInfo()).isEqualTo(VERSION_ACK_EDS); + assertThat(genericXdsConfigEds.getXdsConfig()).isEqualTo(RAW_CLUSTER_LOAD_ASSIGNMENT); } @Test @@ -790,15 +360,6 @@ public void getClientConfigForXdsClient_noSubscribedResources() { verifyClientConfigNode(clientConfig); verifyClientConfigNoResources(clientConfig); } - - private void verifyErrorState(UpdateFailureState errorState) { - // failed_configuration currently not supported. - assertWithMessage("failed_configuration").that(errorState.hasFailedConfiguration()).isFalse(); - assertWithMessage("last_update_attempt").that(errorState.getLastUpdateAttempt()) - .isEqualTo(TIMESTAMP_FAILED_UPDATE); - assertWithMessage("details").that(errorState.getDetails()).isEqualTo(ERROR); - assertWithMessage("version_info").that(errorState.getVersionInfo()).isEqualTo(VERSION_NACK); - } } /** @@ -806,26 +367,10 @@ private void verifyErrorState(UpdateFailureState errorState) { * config dumps correctly, perform a minimal verification of the general shape of ClientConfig. */ private static void verifyClientConfigNoResources(ClientConfig clientConfig) { - // Expect PerXdsConfig for all resource types to be present, but empty. - @SuppressWarnings("deprecation") - int xdsConfigCount = clientConfig.getXdsConfigCount(); - assertThat(xdsConfigCount).isEqualTo(4); - EnumMap configDumps = mapConfigDumps(clientConfig); - assertThat(configDumps.keySet()).containsExactly(LDS, RDS, CDS, EDS); - - ListenersConfigDump listenerConfig = configDumps.get(LDS).getListenerConfig(); - assertThat(listenerConfig.getVersionInfo()).isEqualTo("getCurrentVersion.LDS"); - assertThat(listenerConfig.getDynamicListenersCount()).isEqualTo(0); - - RoutesConfigDump routeConfig = configDumps.get(RDS).getRouteConfig(); - assertThat(routeConfig.getDynamicRouteConfigsCount()).isEqualTo(0); - - ClustersConfigDump clusterConfig = configDumps.get(CDS).getClusterConfig(); - assertThat(clusterConfig.getVersionInfo()).isEqualTo("getCurrentVersion.CDS"); - assertThat(clusterConfig.getDynamicActiveClustersCount()).isEqualTo(0); - - EndpointsConfigDump endpointConfig = configDumps.get(EDS).getEndpointConfig(); - assertThat(endpointConfig.getDynamicEndpointConfigsCount()).isEqualTo(0); + int xdsConfigCount = clientConfig.getGenericXdsConfigsCount(); + assertThat(xdsConfigCount).isEqualTo(0); + EnumMap configDumps = mapConfigDumps(clientConfig); + assertThat(configDumps).isEmpty(); } /** @@ -838,42 +383,18 @@ private static void verifyClientConfigNode(ClientConfig clientConfig) { assertThat(node).isEqualTo(BOOTSTRAP_NODE.toEnvoyProtoNode()); } - /** Verify PerXdsConfig fields that are expected to be omitted. */ - private static void verifyPerXdsConfigEmptyFields(PerXdsConfig perXdsConfig) { - assertThat(perXdsConfig.getStatusValue()).isEqualTo(0); - @SuppressWarnings("deprecation") - int clientStatusValue = perXdsConfig.getClientStatusValue(); - assertThat(clientStatusValue).isEqualTo(0); - } - - private static EnumMap mapConfigDumps(ClientConfig config) { - EnumMap xdsConfigMap = new EnumMap<>(ResourceType.class); - @SuppressWarnings("deprecation") - List xdsConfigList = config.getXdsConfigList(); - for (PerXdsConfig perXdsConfig : xdsConfigList) { - ResourceType type = perXdsConfigToResourceType(perXdsConfig); + private static EnumMap mapConfigDumps(ClientConfig config) { + EnumMap xdsConfigMap = new EnumMap<>(ResourceType.class); + List xdsConfigList = config.getGenericXdsConfigsList(); + for (GenericXdsConfig genericXdsConfig : xdsConfigList) { + ResourceType type = ResourceType.fromTypeUrl(genericXdsConfig.getTypeUrl()); assertThat(type).isNotEqualTo(ResourceType.UNKNOWN); assertThat(xdsConfigMap).doesNotContainKey(type); - xdsConfigMap.put(type, perXdsConfig); + xdsConfigMap.put(type, genericXdsConfig); } return xdsConfigMap; } - private static ResourceType perXdsConfigToResourceType(PerXdsConfig perXdsConfig) { - switch (perXdsConfig.getPerXdsConfigCase()) { - case LISTENER_CONFIG: - return LDS; - case CLUSTER_CONFIG: - return CDS; - case ROUTE_CONFIG: - return RDS; - case ENDPOINT_CONFIG: - return EDS; - default: - return ResourceType.UNKNOWN; - } - } - private static class FakeXdsClientPoolFactory implements XdsClientPoolFactory { @Nullable private final XdsClient xdsClient; From ad0971ef5fa614521e8a6973faa775e87d105e38 Mon Sep 17 00:00:00 2001 From: ZHANG Dapeng Date: Wed, 10 Nov 2021 11:27:42 -0800 Subject: [PATCH 33/50] xds: fix parsing RouteLookupClusterSpecifier mistake (#8641) - Partially revert the change of RlsProtoData.java in #8612 by removing `public` accessor - Have grpc-xds no longer strongly depend on grpc-rls. The application will need grpc-rls as runtime dependencies if they need route lookup feature in xds. - Parse RouteLookupServiceClusterSpecifierPlugin config to the Json/Map representation of `io.grpc.lookup.v1.RouteLookupClusterSpecifier` instead of `io.grpc.rls.RlsProtoData.RouteLookupConfig` --- .../main/java/io/grpc/rls/RlsProtoData.java | 26 ++--- xds/build.gradle | 2 +- .../main/java/io/grpc/xds/MessagePrinter.java | 21 +++- ...teLookupServiceClusterSpecifierPlugin.java | 107 ++++++------------ ...okupServiceClusterSpecifierPluginTest.java | 100 ++++++++-------- 5 files changed, 109 insertions(+), 147 deletions(-) diff --git a/rls/src/main/java/io/grpc/rls/RlsProtoData.java b/rls/src/main/java/io/grpc/rls/RlsProtoData.java index 5192e9cf075..32dc81829b3 100644 --- a/rls/src/main/java/io/grpc/rls/RlsProtoData.java +++ b/rls/src/main/java/io/grpc/rls/RlsProtoData.java @@ -25,7 +25,6 @@ import com.google.common.base.Objects; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import io.grpc.Internal; import io.grpc.rls.RlsProtoData.GrpcKeyBuilder.Name; import java.util.HashSet; import java.util.List; @@ -36,8 +35,7 @@ import javax.annotation.concurrent.Immutable; /** RlsProtoData is a collection of internal representation of RouteLookupService proto messages. */ -@Internal -public final class RlsProtoData { +final class RlsProtoData { private RlsProtoData() {} @@ -142,7 +140,7 @@ public String toString() { /** A config object for gRPC RouteLookupService. */ @Immutable - public static final class RouteLookupConfig { + static final class RouteLookupConfig { private static final long MAX_AGE_MILLIS = TimeUnit.MINUTES.toMillis(5); private static final long MAX_CACHE_SIZE = 5 * 1024 * 1024; @@ -164,8 +162,7 @@ public static final class RouteLookupConfig { @Nullable private final String defaultTarget; - /** Constructor. */ - public RouteLookupConfig( + RouteLookupConfig( List grpcKeyBuilders, String lookupService, long lookupServiceTimeoutInMillis, @@ -336,7 +333,7 @@ private static void checkUniqueName(List grpcKeyBuilders) { * is true, one of the specified names must be present for the keybuilder to match. */ @Immutable - public static final class NameMatcher { + static final class NameMatcher { private final String key; @@ -344,8 +341,7 @@ public static final class NameMatcher { private final boolean optional; - /** Constructor. */ - public NameMatcher(String key, List names, @Nullable Boolean optional) { + NameMatcher(String key, List names, @Nullable Boolean optional) { this.key = checkNotNull(key, "key"); this.names = ImmutableList.copyOf(checkNotNull(names, "names")); this.optional = optional != null ? optional : true; @@ -398,7 +394,7 @@ public String toString() { } /** GrpcKeyBuilder is a configuration to construct headers consumed by route lookup service. */ - public static final class GrpcKeyBuilder { + static final class GrpcKeyBuilder { private final ImmutableList names; @@ -407,7 +403,7 @@ public static final class GrpcKeyBuilder { private final ImmutableMap constantKeys; /** Constructor. All args should be nonnull. Headers should head unique keys. */ - public GrpcKeyBuilder( + GrpcKeyBuilder( List names, List headers, ExtraKeys extraKeys, Map constantKeys) { checkState(names != null && !names.isEmpty(), "names cannot be empty"); @@ -486,7 +482,7 @@ public String toString() { * required and includes the proto package name. The method name may be omitted, in which case * any method on the given service is matched. */ - public static final class Name { + static final class Name { private final String service; @@ -497,7 +493,7 @@ public Name(String service) { } /** The primary constructor. */ - public Name(String service, String method) { + Name(String service, String method) { checkState( !checkNotNull(service, "service").isEmpty(), "service must not be empty or null"); @@ -542,7 +538,7 @@ public String toString() { } @AutoValue - public abstract static class ExtraKeys { + abstract static class ExtraKeys { static final ExtraKeys DEFAULT = create(null, null, null); @Nullable abstract String host(); @@ -551,7 +547,7 @@ public abstract static class ExtraKeys { @Nullable abstract String method(); - public static ExtraKeys create( + static ExtraKeys create( @Nullable String host, @Nullable String service, @Nullable String method) { return new AutoValue_RlsProtoData_ExtraKeys(host, service, method); } diff --git a/xds/build.gradle b/xds/build.gradle index a8dbde3e0a1..3af4f75d62c 100644 --- a/xds/build.gradle +++ b/xds/build.gradle @@ -39,9 +39,9 @@ dependencies { libraries.autovalue_annotation, libraries.opencensus_proto, libraries.protobuf_util - implementation project(path: ':grpc-rls') def nettyDependency = implementation project(':grpc-netty') + testImplementation project(':grpc-rls') testImplementation project(':grpc-core').sourceSets.test.output annotationProcessor libraries.autovalue diff --git a/xds/src/main/java/io/grpc/xds/MessagePrinter.java b/xds/src/main/java/io/grpc/xds/MessagePrinter.java index 249e1f0e965..a2f51443e3d 100644 --- a/xds/src/main/java/io/grpc/xds/MessagePrinter.java +++ b/xds/src/main/java/io/grpc/xds/MessagePrinter.java @@ -16,7 +16,9 @@ package io.grpc.xds; +import com.google.protobuf.Descriptors.Descriptor; import com.google.protobuf.InvalidProtocolBufferException; +import com.google.protobuf.Message; import com.google.protobuf.MessageOrBuilder; import com.google.protobuf.TypeRegistry; import com.google.protobuf.util.JsonFormat; @@ -46,7 +48,7 @@ private static class LazyHolder { static final JsonFormat.Printer printer = newPrinter(); private static JsonFormat.Printer newPrinter() { - TypeRegistry registry = + TypeRegistry.Builder registry = TypeRegistry.newBuilder() .add(Listener.getDescriptor()) .add(io.envoyproxy.envoy.api.v2.Listener.getDescriptor()) @@ -71,9 +73,20 @@ private static JsonFormat.Printer newPrinter() { .add(io.envoyproxy.envoy.config.cluster.aggregate.v2alpha.ClusterConfig .getDescriptor()) .add(ClusterLoadAssignment.getDescriptor()) - .add(io.envoyproxy.envoy.api.v2.ClusterLoadAssignment.getDescriptor()) - .build(); - return JsonFormat.printer().usingTypeRegistry(registry); + .add(io.envoyproxy.envoy.api.v2.ClusterLoadAssignment.getDescriptor()); + try { + @SuppressWarnings("unchecked") + Class routeLookupClusterSpecifierClass = + (Class) + Class.forName("io.grpc.lookup.v1.RouteLookupClusterSpecifier"); + Descriptor descriptor = + (Descriptor) + routeLookupClusterSpecifierClass.getDeclaredMethod("getDescriptor").invoke(null); + registry.add(descriptor); + } catch (Exception e) { + // Ignore. In most cases RouteLookup is not required. + } + return JsonFormat.printer().usingTypeRegistry(registry.build()); } } diff --git a/xds/src/main/java/io/grpc/xds/RouteLookupServiceClusterSpecifierPlugin.java b/xds/src/main/java/io/grpc/xds/RouteLookupServiceClusterSpecifierPlugin.java index 8bec286925f..64464f03737 100644 --- a/xds/src/main/java/io/grpc/xds/RouteLookupServiceClusterSpecifierPlugin.java +++ b/xds/src/main/java/io/grpc/xds/RouteLookupServiceClusterSpecifierPlugin.java @@ -17,18 +17,14 @@ package io.grpc.xds; import com.google.auto.value.AutoValue; +import com.google.common.collect.ImmutableMap; import com.google.protobuf.Any; import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.Message; -import com.google.protobuf.util.Durations; -import io.grpc.lookup.v1.GrpcKeyBuilder; -import io.grpc.lookup.v1.GrpcKeyBuilder.ExtraKeys; -import io.grpc.lookup.v1.GrpcKeyBuilder.Name; -import io.grpc.lookup.v1.NameMatcher; -import io.grpc.lookup.v1.RouteLookupConfig; -import io.grpc.rls.RlsProtoData; -import java.util.ArrayList; -import java.util.List; +import io.grpc.internal.JsonParser; +import io.grpc.internal.JsonUtil; +import java.io.IOException; +import java.util.Map; /** The ClusterSpecifierPlugin for RouteLookup policy. */ final class RouteLookupServiceClusterSpecifierPlugin implements ClusterSpecifierPlugin { @@ -49,84 +45,49 @@ public String[] typeUrls() { } @Override + @SuppressWarnings("unchecked") public ConfigOrError parsePlugin(Message rawProtoMessage) { if (!(rawProtoMessage instanceof Any)) { return ConfigOrError.fromError("Invalid config type: " + rawProtoMessage.getClass()); } - - Any anyMessage = (Any) rawProtoMessage; - RouteLookupConfig configProto; - try { - configProto = anyMessage.unpack(RouteLookupConfig.class); - } catch (InvalidProtocolBufferException e) { - return ConfigOrError.fromError("Invalid proto: " + e); - } try { - List keyBuildersProto = configProto.getGrpcKeybuildersList(); - List keyBuilders = - new ArrayList<>(keyBuildersProto.size()); - for (GrpcKeyBuilder keyBuilderProto : keyBuildersProto) { - List namesProto = keyBuilderProto.getNamesList(); - List names = new ArrayList<>(namesProto.size()); - for (Name nameProto : namesProto) { - if (nameProto.getMethod().isEmpty()) { - names.add(new RlsProtoData.GrpcKeyBuilder.Name(nameProto.getService())); - } else { - names.add( - new RlsProtoData.GrpcKeyBuilder.Name( - nameProto.getService(), nameProto.getMethod())); - } - } - - List headersProto = keyBuilderProto.getHeadersList(); - List headers = new ArrayList<>(headersProto.size()); - for (NameMatcher headerProto : headersProto) { - headers.add( - new RlsProtoData.NameMatcher( - headerProto.getKey(), headerProto.getNamesList(), - headerProto.getRequiredMatch())); - } - - String host = null; - String service = null; - String method = null; - if (keyBuilderProto.hasExtraKeys()) { - ExtraKeys extraKeysProto = keyBuilderProto.getExtraKeys(); - host = extraKeysProto.getHost(); - service = extraKeysProto.getService(); - method = extraKeysProto.getMethod(); - } - RlsProtoData.ExtraKeys extraKeys = - RlsProtoData.ExtraKeys.create(host, service, method); - - RlsProtoData.GrpcKeyBuilder keyBuilder = - new RlsProtoData.GrpcKeyBuilder( - names, headers, extraKeys, keyBuilderProto.getConstantKeysMap()); - keyBuilders.add(keyBuilder); + Any anyMessage = (Any) rawProtoMessage; + Class protoClass; + try { + protoClass = + (Class) + Class.forName("io.grpc.lookup.v1.RouteLookupClusterSpecifier"); + } catch (ClassNotFoundException e) { + return ConfigOrError.fromError("Dependency for 'io.grpc:grpc-rls' is missing: " + e); + } + Message configProto; + try { + configProto = anyMessage.unpack(protoClass); + } catch (InvalidProtocolBufferException e) { + return ConfigOrError.fromError("Invalid proto: " + e); + } + String jsonString = MessagePrinter.print(configProto); + try { + Map jsonMap = (Map) JsonParser.parse(jsonString); + Map config = JsonUtil.getObject(jsonMap, "routeLookupConfig"); + return ConfigOrError.fromConfig(RlsPluginConfig.create(config)); + } catch (IOException e) { + return ConfigOrError.fromError( + "Unable to parse RouteLookupClusterSpecifier: " + jsonString); } - RlsProtoData.RouteLookupConfig config = new RlsProtoData.RouteLookupConfig( - keyBuilders, - configProto.getLookupService(), - Durations.toMillis(configProto.getLookupServiceTimeout()), - configProto.hasMaxAge() ? Durations.toMillis(configProto.getMaxAge()) : null, - configProto.hasStaleAge() ? Durations.toMillis(configProto.getStaleAge()) : null, - configProto.getCacheSizeBytes(), - configProto.getValidTargetsList(), - configProto.getDefaultTarget()); - return ConfigOrError.fromConfig(RlsPluginConfig.create(config)); } catch (RuntimeException e) { - return ConfigOrError.fromError( - "Error parsing RouteLookupConfig: \n" + configProto + "\n reason: " + e); + return ConfigOrError.fromError("Error parsing RouteLookupConfig: " + e); } } @AutoValue abstract static class RlsPluginConfig implements PluginConfig { - abstract RlsProtoData.RouteLookupConfig config(); + abstract ImmutableMap config(); - static RlsPluginConfig create(RlsProtoData.RouteLookupConfig config) { - return new AutoValue_RouteLookupServiceClusterSpecifierPlugin_RlsPluginConfig(config); + static RlsPluginConfig create(Map config) { + return new AutoValue_RouteLookupServiceClusterSpecifierPlugin_RlsPluginConfig( + ImmutableMap.copyOf(config)); } @Override diff --git a/xds/src/test/java/io/grpc/xds/RouteLookupServiceClusterSpecifierPluginTest.java b/xds/src/test/java/io/grpc/xds/RouteLookupServiceClusterSpecifierPluginTest.java index 1bfe4a26f24..f8d1c2ef7c0 100644 --- a/xds/src/test/java/io/grpc/xds/RouteLookupServiceClusterSpecifierPluginTest.java +++ b/xds/src/test/java/io/grpc/xds/RouteLookupServiceClusterSpecifierPluginTest.java @@ -26,8 +26,8 @@ import io.grpc.lookup.v1.GrpcKeyBuilder.ExtraKeys; import io.grpc.lookup.v1.GrpcKeyBuilder.Name; import io.grpc.lookup.v1.NameMatcher; +import io.grpc.lookup.v1.RouteLookupClusterSpecifier; import io.grpc.lookup.v1.RouteLookupConfig; -import io.grpc.rls.RlsProtoData; import io.grpc.xds.RouteLookupServiceClusterSpecifierPlugin.RlsPluginConfig; import org.junit.Test; import org.junit.runner.RunWith; @@ -57,29 +57,38 @@ public void parseConfigWithAllFieldsGiven() { .addValidTargets("valid-target") .setDefaultTarget("default-target") .build(); + RouteLookupClusterSpecifier specifier = + RouteLookupClusterSpecifier.newBuilder().setRouteLookupConfig(routeLookupConfig).build(); RlsPluginConfig config = - RouteLookupServiceClusterSpecifierPlugin.INSTANCE.parsePlugin(Any.pack(routeLookupConfig)) + RouteLookupServiceClusterSpecifierPlugin.INSTANCE.parsePlugin(Any.pack(specifier)) .config; assertThat(config.typeUrl()).isEqualTo("type.googleapis.com/grpc.lookup.v1.RouteLookupConfig"); assertThat(config.config()).isEqualTo( - new RlsProtoData.RouteLookupConfig( - ImmutableList.of( - new RlsProtoData.GrpcKeyBuilder( + ImmutableMap.builder() + .put( + "grpcKeybuilders", + ImmutableList.of(ImmutableMap.of( + "names", ImmutableList.of( - new RlsProtoData.GrpcKeyBuilder.Name("service1", "method1"), - new RlsProtoData.GrpcKeyBuilder.Name("service2", "method2")), + ImmutableMap.of("service", "service1", "method", "method1"), + ImmutableMap.of("service", "service2", "method", "method2")), + "headers", ImmutableList.of( - new RlsProtoData.NameMatcher("key1", ImmutableList.of("v1"), true)), - RlsProtoData.ExtraKeys.create("host1", "service1", "method1"), - ImmutableMap.of("key2", "value2") - )), - "rls-cbt.googleapis.com", - 1234, - 56789L, - 1000L, - 5000, - ImmutableList.of("valid-target"), - "default-target")); + ImmutableMap.of( + "key", "key1", "names", ImmutableList.of("v1"), + "requiredMatch", true)), + "extraKeys", + ImmutableMap.of("host", "host1", "service", "service1", "method", "method1"), + "constantKeys", + ImmutableMap.of("key2", "value2")))) + .put("lookupService", "rls-cbt.googleapis.com") + .put("lookupServiceTimeout", "1.234s") + .put("maxAge", "56.789s") + .put("staleAge", "1s") + .put("cacheSizeBytes", "5000") + .put("validTargets", ImmutableList.of("valid-target")) + .put("defaultTarget","default-target") + .build()); } @Test @@ -96,47 +105,30 @@ public void parseConfigWithOptionalFieldsUnspecified() { .setCacheSizeBytes(5000) .addValidTargets("valid-target") .build(); + RouteLookupClusterSpecifier specifier = + RouteLookupClusterSpecifier.newBuilder().setRouteLookupConfig(routeLookupConfig).build(); RlsPluginConfig config = - RouteLookupServiceClusterSpecifierPlugin.INSTANCE.parsePlugin(Any.pack(routeLookupConfig)) + RouteLookupServiceClusterSpecifierPlugin.INSTANCE.parsePlugin(Any.pack(specifier)) .config; assertThat(config.typeUrl()).isEqualTo("type.googleapis.com/grpc.lookup.v1.RouteLookupConfig"); assertThat(config.config()).isEqualTo( - new RlsProtoData.RouteLookupConfig( - ImmutableList.of( - new RlsProtoData.GrpcKeyBuilder( + ImmutableMap.builder() + .put( + "grpcKeybuilders", + ImmutableList.of(ImmutableMap.of( + "names", ImmutableList.of( - new RlsProtoData.GrpcKeyBuilder.Name("service1"), - new RlsProtoData.GrpcKeyBuilder.Name("service2")), + ImmutableMap.of("service", "service1"), + ImmutableMap.of("service", "service2")), + "headers", ImmutableList.of( - new RlsProtoData.NameMatcher("key1", ImmutableList.of("v1"), true)), - RlsProtoData.ExtraKeys.create(null, null, null), - ImmutableMap.of() - )), - "rls-cbt.googleapis.com", - 1234, - null, - null, - 5000, - ImmutableList.of("valid-target"), - null)); - } - - @Test - public void parseInvalidConfig() { - RouteLookupConfig routeLookupConfig = RouteLookupConfig.newBuilder() - .addGrpcKeybuilders( - GrpcKeyBuilder.newBuilder() - .addNames(Name.newBuilder().setService("service1")) - .addNames(Name.newBuilder().setService("service2")) - .addHeaders( - NameMatcher.newBuilder().setKey("key1").addNames("v1").setRequiredMatch(true))) - .setLookupService("rls-cbt.googleapis.com") - .setLookupServiceTimeout(Durations.fromMillis(1234)) - .setCacheSizeBytes(-5000) // negative - .addValidTargets("valid-target") - .build(); - ConfigOrError configOrError = - RouteLookupServiceClusterSpecifierPlugin.INSTANCE.parsePlugin(Any.pack(routeLookupConfig)); - assertThat(configOrError.errorDetail).contains("cacheSize must be positive"); + ImmutableMap.of( + "key", "key1", "names", ImmutableList.of("v1"), + "requiredMatch", true))))) + .put("lookupService", "rls-cbt.googleapis.com") + .put("lookupServiceTimeout", "1.234s") + .put("cacheSizeBytes", "5000") + .put("validTargets", ImmutableList.of("valid-target")) + .build()); } } From 881747a63df79a7baae252de86447c0997c76f69 Mon Sep 17 00:00:00 2001 From: yifeizhuang Date: Thu, 11 Nov 2021 10:07:14 -0800 Subject: [PATCH 34/50] xds: migrate udpa proto to xds directory (#8686) fix https://github.com/grpc/grpc-java/issues/8631: 1. import udpa protos form new git repo `https://github.com/cncf/xds.git` instead of `https://github.com/cncf/udpa.git` 2. use proto from xds directory not udpa directory in `https://github.com/cncf/xds.git`, details was here https://github.com/cncf/xds/issues/2#issuecomment-875838155 3. support both versions of TypeStruct 4. remove v1 orca service in old directory and use the new one v3, and refer to v3 in ORCA related area --- xds/build.gradle | 7 +- .../service/orca/v3}/OpenRcaServiceGrpc.java | 48 ++++---- .../java/io/grpc/xds/ClientXdsClient.java | 49 +++++--- .../OrcaMetricReportingServerInterceptor.java | 2 +- .../main/java/io/grpc/xds/OrcaOobUtil.java | 6 +- .../java/io/grpc/xds/OrcaPerRequestUtil.java | 2 +- .../io/grpc/xds/ClientXdsClientDataTest.java | 109 ++++++++++++++++++ ...aMetricReportingServerInterceptorTest.java | 2 +- .../java/io/grpc/xds/OrcaOobUtilTest.java | 6 +- .../io/grpc/xds/OrcaPerRequestUtilTest.java | 2 +- xds/third_party/{udpa => xds}/LICENSE | 0 xds/third_party/{udpa => xds}/import.sh | 24 ++-- .../main/proto/udpa/annotations/migrate.proto | 6 + .../proto/udpa/annotations/security.proto | 7 +- .../proto/udpa/annotations/sensitive.proto | 20 ++++ .../main/proto/udpa/annotations/status.proto | 6 + .../proto/udpa/annotations/versioning.proto | 6 + .../proto/udpa/type/v1/typed_struct.proto | 47 ++++++++ .../proto/xds/annotations/v3/migrate.proto | 46 ++++++++ .../proto/xds/annotations/v3/security.proto | 30 +++++ .../proto/xds/annotations/v3}/sensitive.proto | 8 +- .../proto/xds/annotations/v3/status.proto | 59 ++++++++++ .../proto/xds/annotations/v3/versioning.proto | 20 ++++ .../main/proto/xds/core/v3/authority.proto | 7 +- .../proto/xds/core/v3/collection_entry.proto | 7 +- .../proto/xds/core/v3/context_params.proto | 7 +- .../proto/xds/core/v3/resource_locator.proto | 7 +- .../proto/xds/core/v3/resource_name.proto | 7 +- .../xds/data/orca/v3}/orca_load_report.proto | 5 +- .../proto/xds/service/orca/v3}/orca.proto | 9 +- .../proto/xds/type/v3}/typed_struct.proto | 5 +- 31 files changed, 476 insertions(+), 90 deletions(-) rename xds/src/generated/main/grpc/com/github/{udpa/udpa/service/orca/v1 => xds/service/orca/v3}/OpenRcaServiceGrpc.java (83%) rename xds/third_party/{udpa => xds}/LICENSE (100%) rename xds/third_party/{udpa => xds}/import.sh (74%) rename xds/third_party/{udpa => xds}/src/main/proto/udpa/annotations/migrate.proto (86%) rename xds/third_party/{udpa => xds}/src/main/proto/udpa/annotations/security.proto (82%) create mode 100644 xds/third_party/xds/src/main/proto/udpa/annotations/sensitive.proto rename xds/third_party/{udpa => xds}/src/main/proto/udpa/annotations/status.proto (82%) rename xds/third_party/{udpa => xds}/src/main/proto/udpa/annotations/versioning.proto (72%) create mode 100644 xds/third_party/xds/src/main/proto/udpa/type/v1/typed_struct.proto create mode 100644 xds/third_party/xds/src/main/proto/xds/annotations/v3/migrate.proto create mode 100644 xds/third_party/xds/src/main/proto/xds/annotations/v3/security.proto rename xds/third_party/{udpa/src/main/proto/udpa/annotations => xds/src/main/proto/xds/annotations/v3}/sensitive.proto (76%) create mode 100644 xds/third_party/xds/src/main/proto/xds/annotations/v3/status.proto create mode 100644 xds/third_party/xds/src/main/proto/xds/annotations/v3/versioning.proto rename xds/third_party/{udpa => xds}/src/main/proto/xds/core/v3/authority.proto (67%) rename xds/third_party/{udpa => xds}/src/main/proto/xds/core/v3/collection_entry.proto (87%) rename xds/third_party/{udpa => xds}/src/main/proto/xds/core/v3/context_params.proto (76%) rename xds/third_party/{udpa => xds}/src/main/proto/xds/core/v3/resource_locator.proto (95%) rename xds/third_party/{udpa => xds}/src/main/proto/xds/core/v3/resource_name.proto (86%) rename xds/third_party/{udpa/src/main/proto/udpa/data/orca/v1 => xds/src/main/proto/xds/data/orca/v3}/orca_load_report.proto (90%) rename xds/third_party/{udpa/src/main/proto/udpa/service/orca/v1 => xds/src/main/proto/xds/service/orca/v3}/orca.proto (81%) rename xds/third_party/{udpa/src/main/proto/udpa/type/v1 => xds/src/main/proto/xds/type/v3}/typed_struct.proto (94%) diff --git a/xds/build.gradle b/xds/build.gradle index 3af4f75d62c..16b1c877906 100644 --- a/xds/build.gradle +++ b/xds/build.gradle @@ -71,7 +71,7 @@ sourceSets { proto { srcDir 'third_party/envoy/src/main/proto' srcDir 'third_party/protoc-gen-validate/src/main/proto' - srcDir 'third_party/udpa/src/main/proto' + srcDir 'third_party/xds/src/main/proto' srcDir 'third_party/googleapis/src/main/proto' srcDir 'third_party/istio/src/main/proto' } @@ -92,6 +92,7 @@ jar { javadoc { // Exclusions here should generally also be relocated exclude 'com/github/udpa/**' + exclude 'com/github/xds/**' exclude 'com/google/security/**' exclude 'io/envoyproxy/**' // Need to clean up the class structure to reduce how much is exposed @@ -115,6 +116,7 @@ shadowJar { } // Relocated packages commonly need exclusions in jacocoTestReport and javadoc relocate 'com.github.udpa', "${prefixName}.shaded.com.github.udpa" + relocate 'com.github.xds', "${prefixName}.shaded.com.github.xds" relocate 'com.google.api.expr', "${prefixName}.shaded.com.google.api.expr" relocate 'com.google.security', "${prefixName}.shaded.com.google.security" // TODO: missing java_package option in .proto @@ -124,6 +126,7 @@ shadowJar { relocate 'io.netty', 'io.grpc.netty.shaded.io.netty' // TODO: missing java_package option in .proto relocate 'udpa.annotations', "${prefixName}.shaded.udpa.annotations" + relocate 'xds.annotations', "${prefixName}.shaded.xds.annotations" exclude "**/*.proto" } @@ -159,11 +162,13 @@ jacocoTestReport { fileTree(dir: it, exclude: [ // Exclusions here should generally also be relocated '**/com/github/udpa/**', + '**/com/github/xds/**', '**/com/google/api/expr/**', '**/com/google/security/**', '**/envoy/annotations/**', '**/io/envoyproxy/**', '**/udpa/annotations/**', + '**/xds/annotations/**', ]) } } diff --git a/xds/src/generated/main/grpc/com/github/udpa/udpa/service/orca/v1/OpenRcaServiceGrpc.java b/xds/src/generated/main/grpc/com/github/xds/service/orca/v3/OpenRcaServiceGrpc.java similarity index 83% rename from xds/src/generated/main/grpc/com/github/udpa/udpa/service/orca/v1/OpenRcaServiceGrpc.java rename to xds/src/generated/main/grpc/com/github/xds/service/orca/v3/OpenRcaServiceGrpc.java index 78383dba2d3..52ec6898808 100644 --- a/xds/src/generated/main/grpc/com/github/udpa/udpa/service/orca/v1/OpenRcaServiceGrpc.java +++ b/xds/src/generated/main/grpc/com/github/xds/service/orca/v3/OpenRcaServiceGrpc.java @@ -1,4 +1,4 @@ -package com.github.udpa.udpa.service.orca.v1; +package com.github.xds.service.orca.v3; import static io.grpc.MethodDescriptor.generateFullMethodName; @@ -16,38 +16,38 @@ */ @javax.annotation.Generated( value = "by gRPC proto compiler", - comments = "Source: udpa/service/orca/v1/orca.proto") + comments = "Source: xds/service/orca/v3/orca.proto") @io.grpc.stub.annotations.GrpcGenerated public final class OpenRcaServiceGrpc { private OpenRcaServiceGrpc() {} - public static final String SERVICE_NAME = "udpa.service.orca.v1.OpenRcaService"; + public static final String SERVICE_NAME = "xds.service.orca.v3.OpenRcaService"; // Static method descriptors that strictly reflect the proto. - private static volatile io.grpc.MethodDescriptor getStreamCoreMetricsMethod; + private static volatile io.grpc.MethodDescriptor getStreamCoreMetricsMethod; @io.grpc.stub.annotations.RpcMethod( fullMethodName = SERVICE_NAME + '/' + "StreamCoreMetrics", - requestType = com.github.udpa.udpa.service.orca.v1.OrcaLoadReportRequest.class, - responseType = com.github.udpa.udpa.data.orca.v1.OrcaLoadReport.class, + requestType = com.github.xds.service.orca.v3.OrcaLoadReportRequest.class, + responseType = com.github.xds.data.orca.v3.OrcaLoadReport.class, methodType = io.grpc.MethodDescriptor.MethodType.SERVER_STREAMING) - public static io.grpc.MethodDescriptor getStreamCoreMetricsMethod() { - io.grpc.MethodDescriptor getStreamCoreMetricsMethod; + public static io.grpc.MethodDescriptor getStreamCoreMetricsMethod() { + io.grpc.MethodDescriptor getStreamCoreMetricsMethod; if ((getStreamCoreMetricsMethod = OpenRcaServiceGrpc.getStreamCoreMetricsMethod) == null) { synchronized (OpenRcaServiceGrpc.class) { if ((getStreamCoreMetricsMethod = OpenRcaServiceGrpc.getStreamCoreMetricsMethod) == null) { OpenRcaServiceGrpc.getStreamCoreMetricsMethod = getStreamCoreMetricsMethod = - io.grpc.MethodDescriptor.newBuilder() + io.grpc.MethodDescriptor.newBuilder() .setType(io.grpc.MethodDescriptor.MethodType.SERVER_STREAMING) .setFullMethodName(generateFullMethodName(SERVICE_NAME, "StreamCoreMetrics")) .setSampledToLocalTracing(true) .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( - com.github.udpa.udpa.service.orca.v1.OrcaLoadReportRequest.getDefaultInstance())) + com.github.xds.service.orca.v3.OrcaLoadReportRequest.getDefaultInstance())) .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( - com.github.udpa.udpa.data.orca.v1.OrcaLoadReport.getDefaultInstance())) + com.github.xds.data.orca.v3.OrcaLoadReport.getDefaultInstance())) .setSchemaDescriptor(new OpenRcaServiceMethodDescriptorSupplier("StreamCoreMetrics")) .build(); } @@ -116,8 +116,8 @@ public static abstract class OpenRcaServiceImplBase implements io.grpc.BindableS /** */ - public void streamCoreMetrics(com.github.udpa.udpa.service.orca.v1.OrcaLoadReportRequest request, - io.grpc.stub.StreamObserver responseObserver) { + public void streamCoreMetrics(com.github.xds.service.orca.v3.OrcaLoadReportRequest request, + io.grpc.stub.StreamObserver responseObserver) { io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getStreamCoreMetricsMethod(), responseObserver); } @@ -127,8 +127,8 @@ public void streamCoreMetrics(com.github.udpa.udpa.service.orca.v1.OrcaLoadRepor getStreamCoreMetricsMethod(), io.grpc.stub.ServerCalls.asyncServerStreamingCall( new MethodHandlers< - com.github.udpa.udpa.service.orca.v1.OrcaLoadReportRequest, - com.github.udpa.udpa.data.orca.v1.OrcaLoadReport>( + com.github.xds.service.orca.v3.OrcaLoadReportRequest, + com.github.xds.data.orca.v3.OrcaLoadReport>( this, METHODID_STREAM_CORE_METRICS))) .build(); } @@ -160,8 +160,8 @@ protected OpenRcaServiceStub build( /** */ - public void streamCoreMetrics(com.github.udpa.udpa.service.orca.v1.OrcaLoadReportRequest request, - io.grpc.stub.StreamObserver responseObserver) { + public void streamCoreMetrics(com.github.xds.service.orca.v3.OrcaLoadReportRequest request, + io.grpc.stub.StreamObserver responseObserver) { io.grpc.stub.ClientCalls.asyncServerStreamingCall( getChannel().newCall(getStreamCoreMetricsMethod(), getCallOptions()), request, responseObserver); } @@ -193,8 +193,8 @@ protected OpenRcaServiceBlockingStub build( /** */ - public java.util.Iterator streamCoreMetrics( - com.github.udpa.udpa.service.orca.v1.OrcaLoadReportRequest request) { + public java.util.Iterator streamCoreMetrics( + com.github.xds.service.orca.v3.OrcaLoadReportRequest request) { return io.grpc.stub.ClientCalls.blockingServerStreamingCall( getChannel(), getStreamCoreMetricsMethod(), getCallOptions(), request); } @@ -245,8 +245,8 @@ private static final class MethodHandlers implements public void invoke(Req request, io.grpc.stub.StreamObserver responseObserver) { switch (methodId) { case METHODID_STREAM_CORE_METRICS: - serviceImpl.streamCoreMetrics((com.github.udpa.udpa.service.orca.v1.OrcaLoadReportRequest) request, - (io.grpc.stub.StreamObserver) responseObserver); + serviceImpl.streamCoreMetrics((com.github.xds.service.orca.v3.OrcaLoadReportRequest) request, + (io.grpc.stub.StreamObserver) responseObserver); break; default: throw new AssertionError(); @@ -270,7 +270,7 @@ private static abstract class OpenRcaServiceBaseDescriptorSupplier @java.lang.Override public com.google.protobuf.Descriptors.FileDescriptor getFileDescriptor() { - return com.github.udpa.udpa.service.orca.v1.OrcaProto.getDescriptor(); + return com.github.xds.service.orca.v3.OrcaProto.getDescriptor(); } @java.lang.Override diff --git a/xds/src/main/java/io/grpc/xds/ClientXdsClient.java b/xds/src/main/java/io/grpc/xds/ClientXdsClient.java index 4d45dc21d9e..0a11ad47288 100644 --- a/xds/src/main/java/io/grpc/xds/ClientXdsClient.java +++ b/xds/src/main/java/io/grpc/xds/ClientXdsClient.java @@ -162,8 +162,10 @@ final class ClientXdsClient extends XdsClient implements XdsResponseHandler, Res "type.googleapis.com/envoy.config.cluster.aggregate.v2alpha.ClusterConfig"; private static final String TYPE_URL_CLUSTER_CONFIG = "type.googleapis.com/envoy.extensions.clusters.aggregate.v3.ClusterConfig"; - private static final String TYPE_URL_TYPED_STRUCT = + private static final String TYPE_URL_TYPED_STRUCT_UDPA = "type.googleapis.com/udpa.type.v1.TypedStruct"; + private static final String TYPE_URL_TYPED_STRUCT = + "type.googleapis.com/xds.type.v3.TypedStruct"; private static final String TYPE_URL_FILTER_CONFIG = "type.googleapis.com/envoy.config.route.v3.FilterConfig"; // TODO(zdapeng): need to discuss how to handle unsupported values. @@ -908,16 +910,21 @@ static StructOrError parseHttpFilter( } Message rawConfig = httpFilter.getTypedConfig(); String typeUrl = httpFilter.getTypedConfig().getTypeUrl(); - if (typeUrl.equals(TYPE_URL_TYPED_STRUCT)) { - TypedStruct typedStruct; - try { - typedStruct = httpFilter.getTypedConfig().unpack(TypedStruct.class); - } catch (InvalidProtocolBufferException e) { - return StructOrError.fromError( - "HttpFilter [" + filterName + "] contains invalid proto: " + e); + + try { + if (typeUrl.equals(TYPE_URL_TYPED_STRUCT_UDPA)) { + TypedStruct typedStruct = httpFilter.getTypedConfig().unpack(TypedStruct.class); + typeUrl = typedStruct.getTypeUrl(); + rawConfig = typedStruct.getValue(); + } else if (typeUrl.equals(TYPE_URL_TYPED_STRUCT)) { + com.github.xds.type.v3.TypedStruct newTypedStruct = + httpFilter.getTypedConfig().unpack(com.github.xds.type.v3.TypedStruct.class); + typeUrl = newTypedStruct.getTypeUrl(); + rawConfig = newTypedStruct.getValue(); } - typeUrl = typedStruct.getTypeUrl(); - rawConfig = typedStruct.getValue(); + } catch (InvalidProtocolBufferException e) { + return StructOrError.fromError( + "HttpFilter [" + filterName + "] contains invalid proto: " + e); } Filter filter = filterRegistry.get(typeUrl); if ((isForClient && !(filter instanceof ClientInterceptorBuilder)) @@ -991,16 +998,20 @@ static StructOrError> parseOverrideFilterConfigs( typeUrl = anyConfig.getTypeUrl(); } Message rawConfig = anyConfig; - if (typeUrl.equals(TYPE_URL_TYPED_STRUCT)) { - TypedStruct typedStruct; - try { - typedStruct = anyConfig.unpack(TypedStruct.class); - } catch (InvalidProtocolBufferException e) { - return StructOrError.fromError( - "FilterConfig [" + name + "] contains invalid proto: " + e); + try { + if (typeUrl.equals(TYPE_URL_TYPED_STRUCT_UDPA)) { + TypedStruct typedStruct = anyConfig.unpack(TypedStruct.class); + typeUrl = typedStruct.getTypeUrl(); + rawConfig = typedStruct.getValue(); + } else if (typeUrl.equals(TYPE_URL_TYPED_STRUCT)) { + com.github.xds.type.v3.TypedStruct newTypedStruct = + anyConfig.unpack(com.github.xds.type.v3.TypedStruct.class); + typeUrl = newTypedStruct.getTypeUrl(); + rawConfig = newTypedStruct.getValue(); } - typeUrl = typedStruct.getTypeUrl(); - rawConfig = typedStruct.getValue(); + } catch (InvalidProtocolBufferException e) { + return StructOrError.fromError( + "FilterConfig [" + name + "] contains invalid proto: " + e); } Filter filter = filterRegistry.get(typeUrl); if (filter == null) { diff --git a/xds/src/main/java/io/grpc/xds/OrcaMetricReportingServerInterceptor.java b/xds/src/main/java/io/grpc/xds/OrcaMetricReportingServerInterceptor.java index 8a503bd35f9..9c79ed11bc3 100644 --- a/xds/src/main/java/io/grpc/xds/OrcaMetricReportingServerInterceptor.java +++ b/xds/src/main/java/io/grpc/xds/OrcaMetricReportingServerInterceptor.java @@ -16,7 +16,7 @@ package io.grpc.xds; -import com.github.udpa.udpa.data.orca.v1.OrcaLoadReport; +import com.github.xds.data.orca.v3.OrcaLoadReport; import com.google.common.annotations.VisibleForTesting; import io.grpc.Context; import io.grpc.Contexts; diff --git a/xds/src/main/java/io/grpc/xds/OrcaOobUtil.java b/xds/src/main/java/io/grpc/xds/OrcaOobUtil.java index 1b5acd8c080..8970a68bf65 100644 --- a/xds/src/main/java/io/grpc/xds/OrcaOobUtil.java +++ b/xds/src/main/java/io/grpc/xds/OrcaOobUtil.java @@ -22,9 +22,9 @@ import static io.grpc.ConnectivityState.READY; import static io.grpc.ConnectivityState.SHUTDOWN; -import com.github.udpa.udpa.data.orca.v1.OrcaLoadReport; -import com.github.udpa.udpa.service.orca.v1.OpenRcaServiceGrpc; -import com.github.udpa.udpa.service.orca.v1.OrcaLoadReportRequest; +import com.github.xds.data.orca.v3.OrcaLoadReport; +import com.github.xds.service.orca.v3.OpenRcaServiceGrpc; +import com.github.xds.service.orca.v3.OrcaLoadReportRequest; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.MoreObjects; import com.google.common.base.Objects; diff --git a/xds/src/main/java/io/grpc/xds/OrcaPerRequestUtil.java b/xds/src/main/java/io/grpc/xds/OrcaPerRequestUtil.java index 156d53f638e..4b4cff92064 100644 --- a/xds/src/main/java/io/grpc/xds/OrcaPerRequestUtil.java +++ b/xds/src/main/java/io/grpc/xds/OrcaPerRequestUtil.java @@ -18,7 +18,7 @@ import static com.google.common.base.Preconditions.checkNotNull; -import com.github.udpa.udpa.data.orca.v1.OrcaLoadReport; +import com.github.xds.data.orca.v3.OrcaLoadReport; import com.google.common.annotations.VisibleForTesting; import io.grpc.CallOptions; import io.grpc.ClientStreamTracer; diff --git a/xds/src/test/java/io/grpc/xds/ClientXdsClientDataTest.java b/xds/src/test/java/io/grpc/xds/ClientXdsClientDataTest.java index cb7b937bb5f..e14d6db45ac 100644 --- a/xds/src/test/java/io/grpc/xds/ClientXdsClientDataTest.java +++ b/xds/src/test/java/io/grpc/xds/ClientXdsClientDataTest.java @@ -18,14 +18,18 @@ import static com.google.common.truth.Truth.assertThat; +import com.github.xds.type.v3.TypedStruct; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.protobuf.Any; import com.google.protobuf.BoolValue; import com.google.protobuf.InvalidProtocolBufferException; +import com.google.protobuf.Message; import com.google.protobuf.StringValue; +import com.google.protobuf.Struct; import com.google.protobuf.UInt32Value; import com.google.protobuf.UInt64Value; +import com.google.protobuf.Value; import com.google.protobuf.util.Durations; import com.google.re2j.Pattern; import io.envoyproxy.envoy.config.cluster.v3.Cluster; @@ -94,7 +98,9 @@ import io.envoyproxy.envoy.type.v3.FractionalPercent; import io.envoyproxy.envoy.type.v3.FractionalPercent.DenominatorType; import io.envoyproxy.envoy.type.v3.Int64Range; +import io.grpc.ClientInterceptor; import io.grpc.InsecureChannelCredentials; +import io.grpc.LoadBalancer; import io.grpc.Status.Code; import io.grpc.xds.Bootstrapper.ServerInfo; import io.grpc.xds.ClientXdsClient.ResourceInvalidException; @@ -116,7 +122,9 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import javax.annotation.Nullable; import org.junit.After; import org.junit.Before; import org.junit.Rule; @@ -879,6 +887,107 @@ public void parseHttpFilter_unsupportedButOptional() { assertThat(ClientXdsClient.parseHttpFilter(httpFilter, filterRegistry, true)).isNull(); } + private static class SimpleFilterConfig implements FilterConfig { + private final Message message; + + public SimpleFilterConfig(Message rawProtoMessage) { + message = rawProtoMessage; + } + + public Message getConfig() { + return message; + } + + @Override + public String typeUrl() { + return null; + } + } + + private static class TestFilter implements io.grpc.xds.Filter, + io.grpc.xds.Filter.ClientInterceptorBuilder { + @Override + public String[] typeUrls() { + return new String[]{"test-url"}; + } + + @Override + public ConfigOrError parseFilterConfig(Message rawProtoMessage) { + return ConfigOrError.fromConfig(new SimpleFilterConfig(rawProtoMessage)); + } + + @Override + public ConfigOrError parseFilterConfigOverride( + Message rawProtoMessage) { + return ConfigOrError.fromConfig(new SimpleFilterConfig(rawProtoMessage)); + } + + @Nullable + @Override + public ClientInterceptor buildClientInterceptor(FilterConfig config, + @Nullable FilterConfig overrideConfig, + LoadBalancer.PickSubchannelArgs args, + ScheduledExecutorService scheduler) { + return null; + } + } + + @Test + public void parseHttpFilter_typedStructMigration() { + filterRegistry.register(new TestFilter()); + Struct rawStruct = Struct.newBuilder() + .putFields("name", Value.newBuilder().setStringValue("default").build()) + .build(); + HttpFilter httpFilter = HttpFilter.newBuilder() + .setIsOptional(true) + .setTypedConfig(Any.pack( + com.github.udpa.udpa.type.v1.TypedStruct.newBuilder() + .setTypeUrl("test-url") + .setValue(rawStruct) + .build())).build(); + FilterConfig config = ClientXdsClient.parseHttpFilter(httpFilter, filterRegistry, + true).getStruct(); + assertThat(((SimpleFilterConfig)config).getConfig()).isEqualTo(rawStruct); + + HttpFilter httpFilterNewTypeStruct = HttpFilter.newBuilder() + .setIsOptional(true) + .setTypedConfig(Any.pack( + TypedStruct.newBuilder() + .setTypeUrl("test-url") + .setValue(rawStruct) + .build())).build(); + config = ClientXdsClient.parseHttpFilter(httpFilterNewTypeStruct, filterRegistry, + true).getStruct(); + assertThat(((SimpleFilterConfig)config).getConfig()).isEqualTo(rawStruct); + } + + @Test + public void parseOverrideHttpFilter_typedStructMigration() { + filterRegistry.register(new TestFilter()); + Struct rawStruct0 = Struct.newBuilder() + .putFields("name", Value.newBuilder().setStringValue("default0").build()) + .build(); + Struct rawStruct1 = Struct.newBuilder() + .putFields("name", Value.newBuilder().setStringValue("default1").build()) + .build(); + Map rawFilterMap = ImmutableMap.of( + "struct-0", Any.pack( + com.github.udpa.udpa.type.v1.TypedStruct.newBuilder() + .setTypeUrl("test-url") + .setValue(rawStruct0) + .build()), + "struct-1", Any.pack( + TypedStruct.newBuilder() + .setTypeUrl("test-url") + .setValue(rawStruct1) + .build()) + ); + Map map = ClientXdsClient.parseOverrideFilterConfigs(rawFilterMap, + filterRegistry).getStruct(); + assertThat(((SimpleFilterConfig)map.get("struct-0")).getConfig()).isEqualTo(rawStruct0); + assertThat(((SimpleFilterConfig)map.get("struct-1")).getConfig()).isEqualTo(rawStruct1); + } + @Test public void parseHttpFilter_unsupportedAndRequired() { HttpFilter httpFilter = HttpFilter.newBuilder() diff --git a/xds/src/test/java/io/grpc/xds/OrcaMetricReportingServerInterceptorTest.java b/xds/src/test/java/io/grpc/xds/OrcaMetricReportingServerInterceptorTest.java index b59871f5437..4a2074b91d8 100644 --- a/xds/src/test/java/io/grpc/xds/OrcaMetricReportingServerInterceptorTest.java +++ b/xds/src/test/java/io/grpc/xds/OrcaMetricReportingServerInterceptorTest.java @@ -18,7 +18,7 @@ import static com.google.common.truth.Truth.assertThat; -import com.github.udpa.udpa.data.orca.v1.OrcaLoadReport; +import com.github.xds.data.orca.v3.OrcaLoadReport; import io.grpc.CallOptions; import io.grpc.Channel; import io.grpc.ClientCall; diff --git a/xds/src/test/java/io/grpc/xds/OrcaOobUtilTest.java b/xds/src/test/java/io/grpc/xds/OrcaOobUtilTest.java index b3cf80ee617..5f5bc5a69aa 100644 --- a/xds/src/test/java/io/grpc/xds/OrcaOobUtilTest.java +++ b/xds/src/test/java/io/grpc/xds/OrcaOobUtilTest.java @@ -34,9 +34,9 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; -import com.github.udpa.udpa.data.orca.v1.OrcaLoadReport; -import com.github.udpa.udpa.service.orca.v1.OpenRcaServiceGrpc; -import com.github.udpa.udpa.service.orca.v1.OrcaLoadReportRequest; +import com.github.xds.data.orca.v3.OrcaLoadReport; +import com.github.xds.service.orca.v3.OpenRcaServiceGrpc; +import com.github.xds.service.orca.v3.OrcaLoadReportRequest; import com.google.common.util.concurrent.MoreExecutors; import com.google.protobuf.util.Durations; import io.grpc.Attributes; diff --git a/xds/src/test/java/io/grpc/xds/OrcaPerRequestUtilTest.java b/xds/src/test/java/io/grpc/xds/OrcaPerRequestUtilTest.java index bee68c9e634..a6e7c6aca20 100644 --- a/xds/src/test/java/io/grpc/xds/OrcaPerRequestUtilTest.java +++ b/xds/src/test/java/io/grpc/xds/OrcaPerRequestUtilTest.java @@ -27,7 +27,7 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; -import com.github.udpa.udpa.data.orca.v1.OrcaLoadReport; +import com.github.xds.data.orca.v3.OrcaLoadReport; import io.grpc.ClientStreamTracer; import io.grpc.Metadata; import io.grpc.xds.OrcaPerRequestUtil.OrcaPerRequestReportListener; diff --git a/xds/third_party/udpa/LICENSE b/xds/third_party/xds/LICENSE similarity index 100% rename from xds/third_party/udpa/LICENSE rename to xds/third_party/xds/LICENSE diff --git a/xds/third_party/udpa/import.sh b/xds/third_party/xds/import.sh similarity index 74% rename from xds/third_party/udpa/import.sh rename to xds/third_party/xds/import.sh index b96e4ea6da6..36889a52bba 100755 --- a/xds/third_party/udpa/import.sh +++ b/xds/third_party/xds/import.sh @@ -18,21 +18,29 @@ set -e BRANCH=main # import VERSION from one of the google internal CLs -VERSION=5459f2c994033b0afed7e4a70ac7e90c90c1ffee -GIT_REPO="https://github.com/cncf/udpa.git" -GIT_BASE_DIR=udpa -SOURCE_PROTO_BASE_DIR=udpa +VERSION=cb28da3451f158a947dfc45090fe92b07b243bc1 +GIT_REPO="https://github.com/cncf/xds.git" +GIT_BASE_DIR=xds +SOURCE_PROTO_BASE_DIR=xds TARGET_PROTO_BASE_DIR=src/main/proto # Sorted alphabetically. FILES=( udpa/annotations/migrate.proto +xds/annotations/v3/migrate.proto udpa/annotations/security.proto +xds/annotations/v3/security.proto +udpa/annotations/security.proto +xds/annotations/v3/security.proto udpa/annotations/sensitive.proto +xds/annotations/v3/sensitive.proto udpa/annotations/status.proto +xds/annotations/v3/status.proto udpa/annotations/versioning.proto -udpa/data/orca/v1/orca_load_report.proto -udpa/service/orca/v1/orca.proto +xds/annotations/v3/versioning.proto +xds/data/orca/v3/orca_load_report.proto +xds/service/orca/v3/orca.proto udpa/type/v1/typed_struct.proto +xds/type/v3/typed_struct.proto xds/core/v3/authority.proto xds/core/v3/collection_entry.proto xds/core/v3/context_params.proto @@ -40,9 +48,9 @@ xds/core/v3/resource_locator.proto xds/core/v3/resource_name.proto ) -pushd `git rev-parse --show-toplevel`/xds/third_party/udpa +pushd `git rev-parse --show-toplevel`/xds/third_party/xds -# clone the udpa github repo in a tmp directory +# clone the xds github repo in a tmp directory tmpdir="$(mktemp -d)" trap "rm -rf $tmpdir" EXIT diff --git a/xds/third_party/udpa/src/main/proto/udpa/annotations/migrate.proto b/xds/third_party/xds/src/main/proto/udpa/annotations/migrate.proto similarity index 86% rename from xds/third_party/udpa/src/main/proto/udpa/annotations/migrate.proto rename to xds/third_party/xds/src/main/proto/udpa/annotations/migrate.proto index 1c42a6404dc..5289cb8a742 100644 --- a/xds/third_party/udpa/src/main/proto/udpa/annotations/migrate.proto +++ b/xds/third_party/xds/src/main/proto/udpa/annotations/migrate.proto @@ -1,9 +1,15 @@ +// THIS FILE IS DEPRECATED +// Users should instead use the corresponding proto in the xds tree. +// No new changes will be accepted here. + syntax = "proto3"; package udpa.annotations; import "google/protobuf/descriptor.proto"; +option go_package = "github.com/cncf/xds/go/annotations"; + // Magic number in this file derived from top 28bit of SHA256 digest of // "udpa.annotation.migrate". diff --git a/xds/third_party/udpa/src/main/proto/udpa/annotations/security.proto b/xds/third_party/xds/src/main/proto/udpa/annotations/security.proto similarity index 82% rename from xds/third_party/udpa/src/main/proto/udpa/annotations/security.proto rename to xds/third_party/xds/src/main/proto/udpa/annotations/security.proto index 7191fe30c82..52801d30d1e 100644 --- a/xds/third_party/udpa/src/main/proto/udpa/annotations/security.proto +++ b/xds/third_party/xds/src/main/proto/udpa/annotations/security.proto @@ -1,13 +1,16 @@ +// THIS FILE IS DEPRECATED +// Users should instead use the corresponding proto in the xds tree. +// No new changes will be accepted here. + syntax = "proto3"; package udpa.annotations; import "udpa/annotations/status.proto"; -import "google/protobuf/any.proto"; import "google/protobuf/descriptor.proto"; -import "validate/validate.proto"; +option go_package = "github.com/cncf/xds/go/annotations"; // All annotations in this file are experimental and subject to change. Their // only consumer today is the Envoy APIs and SecuritAnnotationValidator protoc diff --git a/xds/third_party/xds/src/main/proto/udpa/annotations/sensitive.proto b/xds/third_party/xds/src/main/proto/udpa/annotations/sensitive.proto new file mode 100644 index 00000000000..ab822fb4884 --- /dev/null +++ b/xds/third_party/xds/src/main/proto/udpa/annotations/sensitive.proto @@ -0,0 +1,20 @@ +// THIS FILE IS DEPRECATED +// Users should instead use the corresponding proto in the xds tree. +// No new changes will be accepted here. + +syntax = "proto3"; + +package udpa.annotations; + +import "google/protobuf/descriptor.proto"; + +option go_package = "github.com/cncf/xds/go/annotations"; + +extend google.protobuf.FieldOptions { + // Magic number is the 28 most significant bits in the sha256sum of "udpa.annotations.sensitive". + // When set to true, `sensitive` indicates that this field contains sensitive data, such as + // personally identifiable information, passwords, or private keys, and should be redacted for + // display by tools aware of this annotation. Note that that this has no effect on standard + // Protobuf functions such as `TextFormat::PrintToString`. + bool sensitive = 76569463; +} diff --git a/xds/third_party/udpa/src/main/proto/udpa/annotations/status.proto b/xds/third_party/xds/src/main/proto/udpa/annotations/status.proto similarity index 82% rename from xds/third_party/udpa/src/main/proto/udpa/annotations/status.proto rename to xds/third_party/xds/src/main/proto/udpa/annotations/status.proto index 9832ffd3a2f..76cfd4dcfef 100644 --- a/xds/third_party/udpa/src/main/proto/udpa/annotations/status.proto +++ b/xds/third_party/xds/src/main/proto/udpa/annotations/status.proto @@ -1,9 +1,15 @@ +// THIS FILE IS DEPRECATED +// Users should instead use the corresponding proto in the xds tree. +// No new changes will be accepted here. + syntax = "proto3"; package udpa.annotations; import "google/protobuf/descriptor.proto"; +option go_package = "github.com/cncf/xds/go/annotations"; + // Magic number in this file derived from top 28bit of SHA256 digest of // "udpa.annotation.status". extend google.protobuf.FileOptions { diff --git a/xds/third_party/udpa/src/main/proto/udpa/annotations/versioning.proto b/xds/third_party/xds/src/main/proto/udpa/annotations/versioning.proto similarity index 72% rename from xds/third_party/udpa/src/main/proto/udpa/annotations/versioning.proto rename to xds/third_party/xds/src/main/proto/udpa/annotations/versioning.proto index 16f6dc30cad..dcb7c85fd4f 100644 --- a/xds/third_party/udpa/src/main/proto/udpa/annotations/versioning.proto +++ b/xds/third_party/xds/src/main/proto/udpa/annotations/versioning.proto @@ -1,9 +1,15 @@ +// THIS FILE IS DEPRECATED +// Users should instead use the corresponding proto in the xds tree. +// No new changes will be accepted here. + syntax = "proto3"; package udpa.annotations; import "google/protobuf/descriptor.proto"; +option go_package = "github.com/cncf/xds/go/annotations"; + extend google.protobuf.MessageOptions { // Magic number derived from 0x78 ('x') 0x44 ('D') 0x53 ('S') VersioningAnnotation versioning = 7881811; diff --git a/xds/third_party/xds/src/main/proto/udpa/type/v1/typed_struct.proto b/xds/third_party/xds/src/main/proto/udpa/type/v1/typed_struct.proto new file mode 100644 index 00000000000..f96625d926f --- /dev/null +++ b/xds/third_party/xds/src/main/proto/udpa/type/v1/typed_struct.proto @@ -0,0 +1,47 @@ +// THIS FILE IS DEPRECATED +// Users should instead use the corresponding proto in the xds tree. +// No new changes will be accepted here. + +syntax = "proto3"; + +package udpa.type.v1; + +option java_outer_classname = "TypedStructProto"; +option java_multiple_files = true; +option java_package = "com.github.udpa.udpa.type.v1"; +option go_package = "github.com/cncf/xds/go/udpa/type/v1"; + +import "validate/validate.proto"; +import "google/protobuf/struct.proto"; + +// A TypedStruct contains an arbitrary JSON serialized protocol buffer message with a URL that +// describes the type of the serialized message. This is very similar to google.protobuf.Any, +// instead of having protocol buffer binary, this employs google.protobuf.Struct as value. +// +// This message is intended to be embedded inside Any, so it shouldn't be directly referred +// from other UDPA messages. +// +// When packing an opaque extension config, packing the expected type into Any is preferred +// wherever possible for its efficiency. TypedStruct should be used only if a proto descriptor +// is not available, for example if: +// - A control plane sends opaque message that is originally from external source in human readable +// format such as JSON or YAML. +// - The control plane doesn't have the knowledge of the protocol buffer schema hence it cannot +// serialize the message in protocol buffer binary format. +// - The DPLB doesn't have have the knowledge of the protocol buffer schema its plugin or extension +// uses. This has to be indicated in the DPLB capability negotiation. +// +// When a DPLB receives a TypedStruct in Any, it should: +// - Check if the type_url of the TypedStruct matches the type the extension expects. +// - Convert value to the type described in type_url and perform validation. +// TODO(lizan): Figure out how TypeStruct should be used with DPLB extensions that doesn't link +// protobuf descriptor with DPLB itself, (e.g. gRPC LB Plugin, Envoy WASM extensions). +message TypedStruct { + // A URL that uniquely identifies the type of the serialize protocol buffer message. + // This has same semantics and format described in google.protobuf.Any: + // https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/any.proto + string type_url = 1; + + // A JSON representation of the above specified type. + google.protobuf.Struct value = 2; +} diff --git a/xds/third_party/xds/src/main/proto/xds/annotations/v3/migrate.proto b/xds/third_party/xds/src/main/proto/xds/annotations/v3/migrate.proto new file mode 100644 index 00000000000..13859274c85 --- /dev/null +++ b/xds/third_party/xds/src/main/proto/xds/annotations/v3/migrate.proto @@ -0,0 +1,46 @@ +syntax = "proto3"; + +package xds.annotations.v3; + +import "google/protobuf/descriptor.proto"; + +option go_package = "github.com/cncf/xds/go/xds/annotations/v3"; + +// Magic number in this file derived from top 28bit of SHA256 digest of +// "xds.annotation.v3.migrate". +extend google.protobuf.MessageOptions { + MigrateAnnotation message_migrate = 112948430; +} +extend google.protobuf.FieldOptions { + FieldMigrateAnnotation field_migrate = 112948430; +} +extend google.protobuf.EnumOptions { + MigrateAnnotation enum_migrate = 112948430; +} +extend google.protobuf.EnumValueOptions { + MigrateAnnotation enum_value_migrate = 112948430; +} +extend google.protobuf.FileOptions { + FileMigrateAnnotation file_migrate = 112948430; +} + +message MigrateAnnotation { + // Rename the message/enum/enum value in next version. + string rename = 1; +} + +message FieldMigrateAnnotation { + // Rename the field in next version. + string rename = 1; + + // Add the field to a named oneof in next version. If this already exists, the + // field will join its siblings under the oneof, otherwise a new oneof will be + // created with the given name. + string oneof_promotion = 2; +} + +message FileMigrateAnnotation { + // Move all types in the file to another package, this implies changing proto + // file path. + string move_to_package = 2; +} diff --git a/xds/third_party/xds/src/main/proto/xds/annotations/v3/security.proto b/xds/third_party/xds/src/main/proto/xds/annotations/v3/security.proto new file mode 100644 index 00000000000..f1f9f40da0a --- /dev/null +++ b/xds/third_party/xds/src/main/proto/xds/annotations/v3/security.proto @@ -0,0 +1,30 @@ +syntax = "proto3"; + +package xds.annotations.v3; + +import "xds/annotations/v3/status.proto"; + +import "google/protobuf/descriptor.proto"; + +option go_package = "github.com/cncf/xds/go/xds/annotations/v3"; + +// All annotations in this file are experimental and subject to change. Their +// only consumer today is the Envoy APIs and SecuritAnnotationValidator protoc +// plugin in this repository. +option (xds.annotations.v3.file_status).work_in_progress = true; + +extend google.protobuf.FieldOptions { + // Magic number is the 28 most significant bits in the sha256sum of + // "xds.annotations.v3.security". + FieldSecurityAnnotation security = 99044135; +} + +// These annotations indicate metadata for the purpose of understanding the +// security significance of fields. +message FieldSecurityAnnotation { + // Field should be set in the presence of untrusted downstreams. + bool configure_for_untrusted_downstream = 1; + + // Field should be set in the presence of untrusted upstreams. + bool configure_for_untrusted_upstream = 2; +} diff --git a/xds/third_party/udpa/src/main/proto/udpa/annotations/sensitive.proto b/xds/third_party/xds/src/main/proto/xds/annotations/v3/sensitive.proto similarity index 76% rename from xds/third_party/udpa/src/main/proto/udpa/annotations/sensitive.proto rename to xds/third_party/xds/src/main/proto/xds/annotations/v3/sensitive.proto index 8dc921f24b5..e2cc0b792ef 100644 --- a/xds/third_party/udpa/src/main/proto/udpa/annotations/sensitive.proto +++ b/xds/third_party/xds/src/main/proto/xds/annotations/v3/sensitive.proto @@ -1,14 +1,16 @@ syntax = "proto3"; -package udpa.annotations; +package xds.annotations.v3; import "google/protobuf/descriptor.proto"; +option go_package = "github.com/cncf/xds/go/xds/annotations/v3"; + extend google.protobuf.FieldOptions { - // Magic number is the 28 most significant bits in the sha256sum of "udpa.annotations.sensitive". + // Magic number is the 28 most significant bits in the sha256sum of "xds.annotations.v3.sensitive". // When set to true, `sensitive` indicates that this field contains sensitive data, such as // personally identifiable information, passwords, or private keys, and should be redacted for // display by tools aware of this annotation. Note that that this has no effect on standard // Protobuf functions such as `TextFormat::PrintToString`. - bool sensitive = 76569463; + bool sensitive = 61008053; } diff --git a/xds/third_party/xds/src/main/proto/xds/annotations/v3/status.proto b/xds/third_party/xds/src/main/proto/xds/annotations/v3/status.proto new file mode 100644 index 00000000000..367e784f671 --- /dev/null +++ b/xds/third_party/xds/src/main/proto/xds/annotations/v3/status.proto @@ -0,0 +1,59 @@ +syntax = "proto3"; + +package xds.annotations.v3; + +import "google/protobuf/descriptor.proto"; + +option go_package = "github.com/cncf/xds/go/xds/annotations/v3"; + +// Magic number in this file derived from top 28bit of SHA256 digest of +// "xds.annotations.v3.status". +extend google.protobuf.FileOptions { + FileStatusAnnotation file_status = 226829418; +} + +extend google.protobuf.MessageOptions { + MessageStatusAnnotation message_status = 226829418; +} + +extend google.protobuf.FieldOptions { + FieldStatusAnnotation field_status = 226829418; +} + +message FileStatusAnnotation { + // The entity is work-in-progress and subject to breaking changes. + bool work_in_progress = 1; +} + +message MessageStatusAnnotation { + // The entity is work-in-progress and subject to breaking changes. + bool work_in_progress = 1; +} + +message FieldStatusAnnotation { + // The entity is work-in-progress and subject to breaking changes. + bool work_in_progress = 1; +} + +enum PackageVersionStatus { + // Unknown package version status. + UNKNOWN = 0; + + // This version of the package is frozen. + FROZEN = 1; + + // This version of the package is the active development version. + ACTIVE = 2; + + // This version of the package is the candidate for the next major version. It + // is typically machine generated from the active development version. + NEXT_MAJOR_VERSION_CANDIDATE = 3; +} + +message StatusAnnotation { + // The entity is work-in-progress and subject to breaking changes. + bool work_in_progress = 1; + + // The entity belongs to a package with the given version status. + PackageVersionStatus package_version_status = 2; +} diff --git a/xds/third_party/xds/src/main/proto/xds/annotations/v3/versioning.proto b/xds/third_party/xds/src/main/proto/xds/annotations/v3/versioning.proto new file mode 100644 index 00000000000..b6440f1949b --- /dev/null +++ b/xds/third_party/xds/src/main/proto/xds/annotations/v3/versioning.proto @@ -0,0 +1,20 @@ +syntax = "proto3"; + +package xds.annotations.v3; + +import "google/protobuf/descriptor.proto"; + +option go_package = "github.com/cncf/xds/go/xds/annotations/v3"; + +extend google.protobuf.MessageOptions { + // Magic number is the 28 most significant bits in the sha256sum of + // "xds.annotations.v3.versioning". + VersioningAnnotation versioning = 92389011; +} + +message VersioningAnnotation { + // Track the previous message type. E.g. this message might be + // xds.foo.v3alpha.Foo and it was previously xds.bar.v2.Bar. This + // information is consumed by UDPA via proto descriptors. + string previous_message_type = 1; +} diff --git a/xds/third_party/udpa/src/main/proto/xds/core/v3/authority.proto b/xds/third_party/xds/src/main/proto/xds/core/v3/authority.proto similarity index 67% rename from xds/third_party/udpa/src/main/proto/xds/core/v3/authority.proto rename to xds/third_party/xds/src/main/proto/xds/core/v3/authority.proto index 49daf4b8cb6..d666c38eafa 100644 --- a/xds/third_party/udpa/src/main/proto/xds/core/v3/authority.proto +++ b/xds/third_party/xds/src/main/proto/xds/core/v3/authority.proto @@ -2,15 +2,16 @@ syntax = "proto3"; package xds.core.v3; -import "udpa/annotations/status.proto"; +import "xds/annotations/v3/status.proto"; import "validate/validate.proto"; option java_outer_classname = "AuthorityProto"; option java_multiple_files = true; -option java_package = "com.github.udpa.xds.core.v3"; +option java_package = "com.github.xds.core.v3"; +option go_package = "github.com/cncf/xds/go/xds/core/v3"; -option (udpa.annotations.file_status).work_in_progress = true; +option (xds.annotations.v3.file_status).work_in_progress = true; // xDS authority information. message Authority { diff --git a/xds/third_party/udpa/src/main/proto/xds/core/v3/collection_entry.proto b/xds/third_party/xds/src/main/proto/xds/core/v3/collection_entry.proto similarity index 87% rename from xds/third_party/udpa/src/main/proto/xds/core/v3/collection_entry.proto rename to xds/third_party/xds/src/main/proto/xds/core/v3/collection_entry.proto index 4ece240ca3e..043a9c421f2 100644 --- a/xds/third_party/udpa/src/main/proto/xds/core/v3/collection_entry.proto +++ b/xds/third_party/xds/src/main/proto/xds/core/v3/collection_entry.proto @@ -4,16 +4,17 @@ package xds.core.v3; import "google/protobuf/any.proto"; -import "udpa/annotations/status.proto"; +import "xds/annotations/v3/status.proto"; import "xds/core/v3/resource_locator.proto"; import "validate/validate.proto"; option java_outer_classname = "CollectionEntryProto"; option java_multiple_files = true; -option java_package = "com.github.udpa.xds.core.v3"; +option java_package = "com.github.xds.core.v3"; +option go_package = "github.com/cncf/xds/go/xds/core/v3"; -option (udpa.annotations.file_status).work_in_progress = true; +option (xds.annotations.v3.file_status).work_in_progress = true; // xDS collection resource wrapper. This encapsulates a xDS resource when // appearing inside a list collection resource. List collection resources are diff --git a/xds/third_party/udpa/src/main/proto/xds/core/v3/context_params.proto b/xds/third_party/xds/src/main/proto/xds/core/v3/context_params.proto similarity index 76% rename from xds/third_party/udpa/src/main/proto/xds/core/v3/context_params.proto rename to xds/third_party/xds/src/main/proto/xds/core/v3/context_params.proto index 8f27c1ca7e5..2a0c079e5d9 100644 --- a/xds/third_party/udpa/src/main/proto/xds/core/v3/context_params.proto +++ b/xds/third_party/xds/src/main/proto/xds/core/v3/context_params.proto @@ -2,13 +2,14 @@ syntax = "proto3"; package xds.core.v3; -import "udpa/annotations/status.proto"; +import "xds/annotations/v3/status.proto"; option java_outer_classname = "ContextParamsProto"; option java_multiple_files = true; -option java_package = "com.github.udpa.xds.core.v3"; +option java_package = "com.github.xds.core.v3"; +option go_package = "github.com/cncf/xds/go/xds/core/v3"; -option (udpa.annotations.file_status).work_in_progress = true; +option (xds.annotations.v3.file_status).work_in_progress = true; // Additional parameters that can be used to select resource variants. These include any // global context parameters, per-resource type client feature capabilities and per-resource diff --git a/xds/third_party/udpa/src/main/proto/xds/core/v3/resource_locator.proto b/xds/third_party/xds/src/main/proto/xds/core/v3/resource_locator.proto similarity index 95% rename from xds/third_party/udpa/src/main/proto/xds/core/v3/resource_locator.proto rename to xds/third_party/xds/src/main/proto/xds/core/v3/resource_locator.proto index d7334394ae2..9b40d52fc43 100644 --- a/xds/third_party/udpa/src/main/proto/xds/core/v3/resource_locator.proto +++ b/xds/third_party/xds/src/main/proto/xds/core/v3/resource_locator.proto @@ -2,16 +2,17 @@ syntax = "proto3"; package xds.core.v3; -import "udpa/annotations/status.proto"; +import "xds/annotations/v3/status.proto"; import "xds/core/v3/context_params.proto"; import "validate/validate.proto"; option java_outer_classname = "ResourceLocatorProto"; option java_multiple_files = true; -option java_package = "com.github.udpa.xds.core.v3"; +option java_package = "com.github.xds.core.v3"; +option go_package = "github.com/cncf/xds/go/xds/core/v3"; -option (udpa.annotations.file_status).work_in_progress = true; +option (xds.annotations.v3.file_status).work_in_progress = true; // xDS resource locators identify a xDS resource name and instruct the // data-plane load balancer on how the resource may be located. diff --git a/xds/third_party/udpa/src/main/proto/xds/core/v3/resource_name.proto b/xds/third_party/xds/src/main/proto/xds/core/v3/resource_name.proto similarity index 86% rename from xds/third_party/udpa/src/main/proto/xds/core/v3/resource_name.proto rename to xds/third_party/xds/src/main/proto/xds/core/v3/resource_name.proto index 103c7de8640..0f3d9974079 100644 --- a/xds/third_party/udpa/src/main/proto/xds/core/v3/resource_name.proto +++ b/xds/third_party/xds/src/main/proto/xds/core/v3/resource_name.proto @@ -2,16 +2,17 @@ syntax = "proto3"; package xds.core.v3; -import "udpa/annotations/status.proto"; +import "xds/annotations/v3/status.proto"; import "xds/core/v3/context_params.proto"; import "validate/validate.proto"; option java_outer_classname = "ResourceNameProto"; option java_multiple_files = true; -option java_package = "com.github.udpa.xds.core.v3"; +option java_package = "com.github.xds.core.v3"; +option go_package = "github.com/cncf/xds/go/xds/core/v3"; -option (udpa.annotations.file_status).work_in_progress = true; +option (xds.annotations.v3.file_status).work_in_progress = true; // xDS resource name. This has a canonical xdstp:// URI representation: // diff --git a/xds/third_party/udpa/src/main/proto/udpa/data/orca/v1/orca_load_report.proto b/xds/third_party/xds/src/main/proto/xds/data/orca/v3/orca_load_report.proto similarity index 90% rename from xds/third_party/udpa/src/main/proto/udpa/data/orca/v1/orca_load_report.proto rename to xds/third_party/xds/src/main/proto/xds/data/orca/v3/orca_load_report.proto index 3105b094724..9b8f03827dc 100644 --- a/xds/third_party/udpa/src/main/proto/udpa/data/orca/v1/orca_load_report.proto +++ b/xds/third_party/xds/src/main/proto/xds/data/orca/v3/orca_load_report.proto @@ -1,10 +1,11 @@ syntax = "proto3"; -package udpa.data.orca.v1; +package xds.data.orca.v3; option java_outer_classname = "OrcaLoadReportProto"; option java_multiple_files = true; -option java_package = "com.github.udpa.udpa.data.orca.v1"; +option java_package = "com.github.xds.data.orca.v3"; +option go_package = "github.com/cncf/xds/go/xds/data/orca/v3"; import "validate/validate.proto"; diff --git a/xds/third_party/udpa/src/main/proto/udpa/service/orca/v1/orca.proto b/xds/third_party/xds/src/main/proto/xds/service/orca/v3/orca.proto similarity index 81% rename from xds/third_party/udpa/src/main/proto/udpa/service/orca/v1/orca.proto rename to xds/third_party/xds/src/main/proto/xds/service/orca/v3/orca.proto index d48cccfeacb..addf62f1ce5 100644 --- a/xds/third_party/udpa/src/main/proto/udpa/service/orca/v1/orca.proto +++ b/xds/third_party/xds/src/main/proto/xds/service/orca/v3/orca.proto @@ -1,12 +1,13 @@ syntax = "proto3"; -package udpa.service.orca.v1; +package xds.service.orca.v3; option java_outer_classname = "OrcaProto"; option java_multiple_files = true; -option java_package = "com.github.udpa.udpa.service.orca.v1"; +option java_package = "com.github.xds.service.orca.v3"; +option go_package = "github.com/cncf/xds/go/xds/service/orca/v3"; -import "udpa/data/orca/v1/orca_load_report.proto"; +import "xds/data/orca/v3/orca_load_report.proto"; import "google/protobuf/duration.proto"; @@ -24,7 +25,7 @@ import "validate/validate.proto"; // streaming service, client needs to terminate current RPC and initiate // a new call to change backend reporting frequency. service OpenRcaService { - rpc StreamCoreMetrics(OrcaLoadReportRequest) returns (stream udpa.data.orca.v1.OrcaLoadReport); + rpc StreamCoreMetrics(OrcaLoadReportRequest) returns (stream xds.data.orca.v3.OrcaLoadReport); } message OrcaLoadReportRequest { diff --git a/xds/third_party/udpa/src/main/proto/udpa/type/v1/typed_struct.proto b/xds/third_party/xds/src/main/proto/xds/type/v3/typed_struct.proto similarity index 94% rename from xds/third_party/udpa/src/main/proto/udpa/type/v1/typed_struct.proto rename to xds/third_party/xds/src/main/proto/xds/type/v3/typed_struct.proto index e46a2cb5757..5d7226c4bf6 100644 --- a/xds/third_party/udpa/src/main/proto/udpa/type/v1/typed_struct.proto +++ b/xds/third_party/xds/src/main/proto/xds/type/v3/typed_struct.proto @@ -1,10 +1,11 @@ syntax = "proto3"; -package udpa.type.v1; +package xds.type.v3; option java_outer_classname = "TypedStructProto"; option java_multiple_files = true; -option java_package = "com.github.udpa.udpa.type.v1"; +option java_package = "com.github.xds.type.v3"; +option go_package = "github.com/cncf/xds/go/xds/type/v3"; import "validate/validate.proto"; import "google/protobuf/struct.proto"; From 71c5eb07d3e618d1ee22b87946390d6e67c8492d Mon Sep 17 00:00:00 2001 From: yifeizhuang Date: Fri, 12 Nov 2021 16:54:35 -0800 Subject: [PATCH 35/50] Update README etc to reference 1.42.1 (#8694) --- README.md | 36 ++++++++++++------------ cronet/README.md | 2 +- documentation/android-channel-builder.md | 4 +-- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index aa3c61b9c47..d11c2850805 100644 --- a/README.md +++ b/README.md @@ -31,8 +31,8 @@ For a guided tour, take a look at the [quick start guide](https://grpc.io/docs/languages/java/quickstart) or the more explanatory [gRPC basics](https://grpc.io/docs/languages/java/basics). -The [examples](https://github.com/grpc/grpc-java/tree/v1.42.0/examples) and the -[Android example](https://github.com/grpc/grpc-java/tree/v1.42.0/examples/android) +The [examples](https://github.com/grpc/grpc-java/tree/v1.42.1/examples) and the +[Android example](https://github.com/grpc/grpc-java/tree/v1.42.1/examples/android) are standalone projects that showcase the usage of gRPC. Download @@ -43,17 +43,17 @@ Download [the JARs][]. Or for Maven with non-Android, add to your `pom.xml`: io.grpc grpc-netty-shaded - 1.42.0 + 1.42.1 io.grpc grpc-protobuf - 1.42.0 + 1.42.1 io.grpc grpc-stub - 1.42.0 + 1.42.1 org.apache.tomcat @@ -65,23 +65,23 @@ Download [the JARs][]. Or for Maven with non-Android, add to your `pom.xml`: Or for Gradle with non-Android, add to your dependencies: ```gradle -implementation 'io.grpc:grpc-netty-shaded:1.42.0' -implementation 'io.grpc:grpc-protobuf:1.42.0' -implementation 'io.grpc:grpc-stub:1.42.0' +implementation 'io.grpc:grpc-netty-shaded:1.42.1' +implementation 'io.grpc:grpc-protobuf:1.42.1' +implementation 'io.grpc:grpc-stub:1.42.1' compileOnly 'org.apache.tomcat:annotations-api:6.0.53' // necessary for Java 9+ ``` For Android client, use `grpc-okhttp` instead of `grpc-netty-shaded` and `grpc-protobuf-lite` instead of `grpc-protobuf`: ```gradle -implementation 'io.grpc:grpc-okhttp:1.42.0' -implementation 'io.grpc:grpc-protobuf-lite:1.42.0' -implementation 'io.grpc:grpc-stub:1.42.0' +implementation 'io.grpc:grpc-okhttp:1.42.1' +implementation 'io.grpc:grpc-protobuf-lite:1.42.1' +implementation 'io.grpc:grpc-stub:1.42.1' compileOnly 'org.apache.tomcat:annotations-api:6.0.53' // necessary for Java 9+ ``` [the JARs]: -https://search.maven.org/search?q=g:io.grpc%20AND%20v:1.42.0 +https://search.maven.org/search?q=g:io.grpc%20AND%20v:1.42.1 Development snapshots are available in [Sonatypes's snapshot repository](https://oss.sonatype.org/content/repositories/snapshots/). @@ -111,9 +111,9 @@ For protobuf-based codegen integrated with the Maven build system, you can use protobuf-maven-plugin 0.6.1 - com.google.protobuf:protoc:3.17.3:exe:${os.detected.classifier} + com.google.protobuf:protoc:3.17.2:exe:${os.detected.classifier} grpc-java - io.grpc:protoc-gen-grpc-java:1.42.0:exe:${os.detected.classifier} + io.grpc:protoc-gen-grpc-java:1.42.1:exe:${os.detected.classifier} @@ -139,11 +139,11 @@ plugins { protobuf { protoc { - artifact = "com.google.protobuf:protoc:3.17.3" + artifact = "com.google.protobuf:protoc:3.17.2" } plugins { grpc { - artifact = 'io.grpc:protoc-gen-grpc-java:1.42.0' + artifact = 'io.grpc:protoc-gen-grpc-java:1.42.1' } } generateProtoTasks { @@ -172,11 +172,11 @@ plugins { protobuf { protoc { - artifact = "com.google.protobuf:protoc:3.17.3" + artifact = "com.google.protobuf:protoc:3.17.2" } plugins { grpc { - artifact = 'io.grpc:protoc-gen-grpc-java:1.42.0' + artifact = 'io.grpc:protoc-gen-grpc-java:1.42.1' } } generateProtoTasks { diff --git a/cronet/README.md b/cronet/README.md index 9a9f8fbe6c4..a682c5bcee8 100644 --- a/cronet/README.md +++ b/cronet/README.md @@ -26,7 +26,7 @@ In your app module's `build.gradle` file, include a dependency on both `grpc-cro Google Play Services Client Library for Cronet ``` -implementation 'io.grpc:grpc-cronet:1.42.0' +implementation 'io.grpc:grpc-cronet:1.42.1' implementation 'com.google.android.gms:play-services-cronet:16.0.0' ``` diff --git a/documentation/android-channel-builder.md b/documentation/android-channel-builder.md index 7e56e391038..d9541c56623 100644 --- a/documentation/android-channel-builder.md +++ b/documentation/android-channel-builder.md @@ -36,8 +36,8 @@ In your `build.gradle` file, include a dependency on both `grpc-android` and `grpc-okhttp`: ``` -implementation 'io.grpc:grpc-android:1.42.0' -implementation 'io.grpc:grpc-okhttp:1.42.0' +implementation 'io.grpc:grpc-android:1.42.1' +implementation 'io.grpc:grpc-okhttp:1.42.1' ``` You also need permission to access the device's network state in your From b746bab97b6910aa55bd8f0a40d8f5b8b51501cb Mon Sep 17 00:00:00 2001 From: sanjaypujare Date: Fri, 12 Nov 2021 17:40:07 -0800 Subject: [PATCH 36/50] buildscripts: rename xds-k8s to psm-security as part of tech-debt cleanup and name clarity (#8695) --- buildscripts/kokoro/{xds-k8s.cfg => psm-security.cfg} | 2 +- buildscripts/kokoro/{xds-k8s.sh => psm-security.sh} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename buildscripts/kokoro/{xds-k8s.cfg => psm-security.cfg} (81%) rename buildscripts/kokoro/{xds-k8s.sh => psm-security.sh} (100%) diff --git a/buildscripts/kokoro/xds-k8s.cfg b/buildscripts/kokoro/psm-security.cfg similarity index 81% rename from buildscripts/kokoro/xds-k8s.cfg rename to buildscripts/kokoro/psm-security.cfg index bdbd968d7a3..f2cfd7babff 100644 --- a/buildscripts/kokoro/xds-k8s.cfg +++ b/buildscripts/kokoro/psm-security.cfg @@ -1,7 +1,7 @@ # Config file for internal CI # Location of the continuous shell script in repository. -build_file: "grpc-java/buildscripts/kokoro/xds-k8s.sh" +build_file: "grpc-java/buildscripts/kokoro/psm-security.sh" timeout_mins: 180 action { diff --git a/buildscripts/kokoro/xds-k8s.sh b/buildscripts/kokoro/psm-security.sh similarity index 100% rename from buildscripts/kokoro/xds-k8s.sh rename to buildscripts/kokoro/psm-security.sh From a2398ce5dbe2eacf841d7af9c9aa5e912e8db0aa Mon Sep 17 00:00:00 2001 From: Mohan Li <67390330+mohanli-ml@users.noreply.github.com> Date: Mon, 15 Nov 2021 15:46:56 -0800 Subject: [PATCH 37/50] alts: Make GoogleDefaultChannelCredentials take a CallCredentials (#8548) DirectPath is going to support non-default service account. This commit allows users to pass CallCredentials to GoogleDefaultChannelCredentials. See design in go/directpath-file-credential-google-default-creds --- .../alts/GoogleDefaultChannelCredentials.java | 79 +++++++++++++------ 1 file changed, 57 insertions(+), 22 deletions(-) diff --git a/alts/src/main/java/io/grpc/alts/GoogleDefaultChannelCredentials.java b/alts/src/main/java/io/grpc/alts/GoogleDefaultChannelCredentials.java index b7d79435f00..fe8c005da45 100644 --- a/alts/src/main/java/io/grpc/alts/GoogleDefaultChannelCredentials.java +++ b/alts/src/main/java/io/grpc/alts/GoogleDefaultChannelCredentials.java @@ -44,30 +44,65 @@ private GoogleDefaultChannelCredentials() {} * as fallback. */ public static ChannelCredentials create() { - ChannelCredentials nettyCredentials = - InternalNettyChannelCredentials.create(createClientFactory()); - CallCredentials callCredentials; - try { - callCredentials = MoreCallCredentials.from(GoogleCredentials.getApplicationDefault()); - } catch (IOException e) { - callCredentials = new FailingCallCredentials( - Status.UNAUTHENTICATED - .withDescription("Failed to get Google default credentials") - .withCause(e)); - } - return CompositeChannelCredentials.create(nettyCredentials, callCredentials); + return newBuilder().build(); } - private static InternalProtocolNegotiator.ClientFactory createClientFactory() { - SslContext sslContext; - try { - sslContext = GrpcSslContexts.forClient().build(); - } catch (SSLException e) { - throw new RuntimeException(e); + /** + * Returns a new instance of {@link Builder}. + * + * @since 1.42.0 + */ + public static Builder newBuilder() { + return new Builder(); + } + + /** + * Builder for {@link GoogleDefaultChannelCredentials} instances. + * + * @since 1.42.0 + */ + public static final class Builder { + private CallCredentials callCredentials; + + private Builder() {} + + /** Constructs GoogleDefaultChannelCredentials with a given call credential. */ + public Builder callCredentials(CallCredentials callCreds) { + callCredentials = callCreds; + return this; + } + + /** Builds a GoogleDefaultChannelCredentials instance. */ + public ChannelCredentials build() { + ChannelCredentials nettyCredentials = + InternalNettyChannelCredentials.create(createClientFactory()); + if (callCredentials != null) { + return CompositeChannelCredentials.create(nettyCredentials, callCredentials); + } + CallCredentials callCreds; + try { + callCreds = MoreCallCredentials.from(GoogleCredentials.getApplicationDefault()); + } catch (IOException e) { + callCreds = + new FailingCallCredentials( + Status.UNAUTHENTICATED + .withDescription("Failed to get Google default credentials") + .withCause(e)); + } + return CompositeChannelCredentials.create(nettyCredentials, callCreds); + } + + private static InternalProtocolNegotiator.ClientFactory createClientFactory() { + SslContext sslContext; + try { + sslContext = GrpcSslContexts.forClient().build(); + } catch (SSLException e) { + throw new RuntimeException(e); + } + return new GoogleDefaultProtocolNegotiatorFactory( + /* targetServiceAccounts= */ ImmutableList.of(), + SharedResourcePool.forResource(HandshakerServiceChannel.SHARED_HANDSHAKER_CHANNEL), + sslContext); } - return new GoogleDefaultProtocolNegotiatorFactory( - /* targetServiceAccounts= */ ImmutableList.of(), - SharedResourcePool.forResource(HandshakerServiceChannel.SHARED_HANDSHAKER_CHANNEL), - sslContext); } } From 5a3b8e2141a9992f71361bd605ebc6f1184742e2 Mon Sep 17 00:00:00 2001 From: beatrausch <30717474+beatrausch@users.noreply.github.com> Date: Wed, 17 Nov 2021 03:29:29 +0100 Subject: [PATCH 38/50] okhttp: introduced new TLS1.2 cipher suites and internal okhttp implementation for TLS1.3 prepared (#8650) This introduces new TLS 1.2 cipher suites (#8610) and prepares the internal okhttp implementation for TLS1.3. A new method for creating internal ConnectionSpec was added to be able to use the newly introduced cipher suites in the OkHttpChannelBuilder. Okhttp cipher suites synchronized with the ones from netty. --- .../io/grpc/okhttp/OkHttpChannelBuilder.java | 47 +++++++++++++++--- .../io/grpc/okhttp/internal/CipherSuite.java | 22 +++++++++ .../grpc/okhttp/internal/ConnectionSpec.java | 49 ++++++++++--------- .../io/grpc/okhttp/internal/TlsVersion.java | 5 +- 4 files changed, 92 insertions(+), 31 deletions(-) diff --git a/okhttp/src/main/java/io/grpc/okhttp/OkHttpChannelBuilder.java b/okhttp/src/main/java/io/grpc/okhttp/OkHttpChannelBuilder.java index af5ebe2886c..b3e90d158fa 100644 --- a/okhttp/src/main/java/io/grpc/okhttp/OkHttpChannelBuilder.java +++ b/okhttp/src/main/java/io/grpc/okhttp/OkHttpChannelBuilder.java @@ -105,15 +105,22 @@ private enum NegotiationType { new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) .cipherSuites( // The following items should be sync with Netty's Http2SecurityUtil.CIPHERS. - CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, - CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256, - CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, - CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384) - .tlsVersions(TlsVersion.TLS_1_2) + CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 + + // TLS 1.3 does not work so far. See issues: + // https://github.com/grpc/grpc-java/issues/7765 + // + // TLS 1.3 + //CipherSuite.TLS_AES_128_GCM_SHA256, + //CipherSuite.TLS_AES_256_GCM_SHA384, + //CipherSuite.TLS_CHACHA20_POLY1305_SHA256 + ) + .tlsVersions(/*TlsVersion.TLS_1_3,*/ TlsVersion.TLS_1_2) .supportsTlsExtensions(true) .build(); @@ -396,6 +403,32 @@ public OkHttpChannelBuilder connectionSpec( return this; } + /** + * Sets the connection specification used for secure connections. + * + *

    By default a modern, HTTP/2-compatible spec will be used. + * + *

    This method is only used when building a secure connection. For plaintext + * connection, use {@link #usePlaintext()} instead. + * + * @param tlsVersions List of tls versions. + * @param cipherSuites List of cipher suites. + */ + public OkHttpChannelBuilder tlsConnectionSpec( + String[] tlsVersions, String[] cipherSuites) { + Preconditions.checkState(!freezeSecurityConfiguration, + "Cannot change security when using ChannelCredentials"); + Preconditions.checkNotNull(tlsVersions, "tls versions must not null"); + Preconditions.checkNotNull(cipherSuites, "ciphers must not null"); + + this.connectionSpec = new ConnectionSpec.Builder(true) + .supportsTlsExtensions(true) + .tlsVersions(tlsVersions) + .cipherSuites(cipherSuites) + .build(); + return this; + } + /** Sets the negotiation type for the HTTP/2 connection to plaintext. */ @Override public OkHttpChannelBuilder usePlaintext() { diff --git a/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/CipherSuite.java b/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/CipherSuite.java index 70fa796f248..1a9aab284bb 100644 --- a/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/CipherSuite.java +++ b/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/CipherSuite.java @@ -354,6 +354,22 @@ public enum CipherSuite { // TLS_ECDHE_ECDSA_WITH_AES_256_CCM("TLS_ECDHE_ECDSA_WITH_AES_256_CCM", 0xc0ad, 7251, MAX_VALUE, MAX_VALUE), // TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8("TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8", 0xc0ae, 7251, MAX_VALUE, MAX_VALUE), // TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8("TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8", 0xc0af, 7251, MAX_VALUE, MAX_VALUE), + + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256("TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", 0xcca8), + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256("TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", 0xcca9), + TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256("TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256", 0xccaa), + // TLS_PSK_WITH_CHACHA20_POLY1305_SHA256("TLS_PSK_WITH_CHACHA20_POLY1305_SHA256", 0xccab), + TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256("TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256", 0xccac), + // TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256("TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256", 0xccad), + // TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256("TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256", 0xccae), + + // TLS 1.3 https://tools.ietf.org/html/rfc8446 + TLS_AES_128_GCM_SHA256("TLS_AES_128_GCM_SHA256", 0x1301), + TLS_AES_256_GCM_SHA384("TLS_AES_256_GCM_SHA384", 0x1302), + TLS_CHACHA20_POLY1305_SHA256("TLS_CHACHA20_POLY1305_SHA256", 0x1303), + TLS_AES_128_CCM_SHA256("TLS_AES_128_CCM_SHA256", 0x1304), + TLS_AES_128_CCM_8_SHA256("TLS_AES_128_CCM_8_SHA256", 0x1305), + ; final String javaName; @@ -372,6 +388,12 @@ private CipherSuite( this.javaName = javaName; } + @SuppressWarnings("UnusedVariable") + private CipherSuite( + String javaName, int value) { + this.javaName = javaName; + } + public static CipherSuite forJavaName(String javaName) { return javaName.startsWith("SSL_") ? valueOf("TLS_" + javaName.substring(4)) diff --git a/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/ConnectionSpec.java b/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/ConnectionSpec.java index 457e9c301f3..b84a1ff94ee 100644 --- a/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/ConnectionSpec.java +++ b/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/ConnectionSpec.java @@ -30,40 +30,43 @@ */ public final class ConnectionSpec { - // This is a subset of the cipher suites supported in Chrome 37, current as of 2014-10-5. - // All of these suites are available on Android 5.0; earlier releases support a subset of - // these suites. https://github.com/square/okhttp/issues/330 + // This is nearly equal to the cipher suites supported in Chrome 72, current as of 2019-02-24. + // See https://tinyurl.com/okhttp-cipher-suites for availability. private static final CipherSuite[] APPROVED_CIPHER_SUITES = new CipherSuite[] { - CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, - - // Note that the following cipher suites are all on HTTP/2's bad cipher suites list. We'll - // continue to include them until better suites are commonly available. For example, none - // of the better cipher suites listed above shipped with Android 4.4 or Java 7. - CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, - CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, - CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, - CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, - CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA, - CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA, - CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA, - CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256, - CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, - CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA, - CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA, + // TLSv1.3. + CipherSuite.TLS_AES_128_GCM_SHA256, + CipherSuite.TLS_AES_256_GCM_SHA384, + CipherSuite.TLS_CHACHA20_POLY1305_SHA256, + + // TLSv1.0, TLSv1.1, TLSv1.2. + CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + + // Note that the following cipher suites are all on HTTP/2's bad cipher suites list. We'll + // continue to include them until better suites are commonly available. + CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384, + CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, + CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA, + CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA }; /** A modern TLS connection with extensions like SNI and ALPN available. */ public static final ConnectionSpec MODERN_TLS = new Builder(true) .cipherSuites(APPROVED_CIPHER_SUITES) - .tlsVersions(TlsVersion.TLS_1_2, TlsVersion.TLS_1_1, TlsVersion.TLS_1_0) + .tlsVersions(TlsVersion.TLS_1_3, TlsVersion.TLS_1_2) .supportsTlsExtensions(true) .build(); /** A backwards-compatible fallback connection for interop with obsolete servers. */ public static final ConnectionSpec COMPATIBLE_TLS = new Builder(MODERN_TLS) - .tlsVersions(TlsVersion.TLS_1_0) + .tlsVersions(TlsVersion.TLS_1_3, TlsVersion.TLS_1_2, TlsVersion.TLS_1_1, TlsVersion.TLS_1_0) .supportsTlsExtensions(true) .build(); diff --git a/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/TlsVersion.java b/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/TlsVersion.java index 548f4acbc5b..6692c80fad4 100644 --- a/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/TlsVersion.java +++ b/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/TlsVersion.java @@ -26,6 +26,7 @@ * {@link SSLSocket#setEnabledProtocols}. */ public enum TlsVersion { + TLS_1_3("TLSv1.3"), // 2016. TLS_1_2("TLSv1.2"), // 2008. TLS_1_1("TLSv1.1"), // 2006. TLS_1_0("TLSv1"), // 1999. @@ -39,7 +40,9 @@ private TlsVersion(String javaName) { } public static TlsVersion forJavaName(String javaName) { - if ("TLSv1.2".equals(javaName)) { + if ("TLSv1.3".equals(javaName)) { + return TLS_1_3; + } else if ("TLSv1.2".equals(javaName)) { return TLS_1_2; } else if ("TLSv1.1".equals(javaName)) { return TLS_1_1; From dd0db6cf41df21965bb7c0f166cdf279c31290ad Mon Sep 17 00:00:00 2001 From: ZHANG Dapeng Date: Wed, 17 Nov 2021 19:54:40 -0800 Subject: [PATCH 39/50] xds: terminate XdsServer start() thread when shutdownNow() is called `XdsServerWrapper.start()` [blocks](https://github.com/grpc/grpc-java/blob/master/xds/src/main/java/io/grpc/xds/XdsServerWrapper.java#L162) until `LdsResourceWatcher`'s callback is called. If no callback is called due to whatever issue of the XdsClient, the server start() will be stuck forever, even we call `shutdownNow()`. Changing the `shutdownNow()` behavior to unblock `start()` immediately. --- .../java/io/grpc/xds/XdsServerWrapper.java | 1 + .../io/grpc/xds/XdsServerWrapperTest.java | 33 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/xds/src/main/java/io/grpc/xds/XdsServerWrapper.java b/xds/src/main/java/io/grpc/xds/XdsServerWrapper.java index dab3cd798f7..6eefcf63411 100644 --- a/xds/src/main/java/io/grpc/xds/XdsServerWrapper.java +++ b/xds/src/main/java/io/grpc/xds/XdsServerWrapper.java @@ -224,6 +224,7 @@ public void run() { delegate.shutdownNow(); } internalShutdown(); + initialStartFuture.set(new IOException("server is forcefully shut down")); } }); return this; diff --git a/xds/src/test/java/io/grpc/xds/XdsServerWrapperTest.java b/xds/src/test/java/io/grpc/xds/XdsServerWrapperTest.java index 1bd102db42d..bfb16c9745e 100644 --- a/xds/src/test/java/io/grpc/xds/XdsServerWrapperTest.java +++ b/xds/src/test/java/io/grpc/xds/XdsServerWrapperTest.java @@ -76,6 +76,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicReference; +import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -116,6 +117,11 @@ selectorManager, new FakeXdsClientPoolFactory(xdsClient), filterRegistry, executor.getScheduledExecutorService()); } + @After + public void tearDown() { + xdsServerWrapper.shutdownNow(); + } + @Test public void testBootstrap_notV3() throws Exception { Bootstrapper.BootstrapInfo b = @@ -324,6 +330,33 @@ public void run() { assertThat(xdsServerWrapper.isTerminated()).isTrue(); } + @Test + public void shutdownNow_startThreadShouldNotLeak() throws Exception { + final SettableFuture start = SettableFuture.create(); + Executors.newSingleThreadExecutor() + .execute( + new Runnable() { + @Override + public void run() { + try { + start.set(xdsServerWrapper.start()); + } catch (Exception ex) { + start.setException(ex); + } + } + }); + assertThat(xdsClient.ldsResource.get(5, TimeUnit.SECONDS)) + .isEqualTo("grpc/server?udpa.resource.listening_address=0.0.0.0:1"); + xdsServerWrapper.shutdownNow(); + try { + start.get(5, TimeUnit.SECONDS); + fail("should have thrown but not"); + } catch (ExecutionException ex) { + assertThat(ex).hasCauseThat().isInstanceOf(IOException.class); + assertThat(ex).hasCauseThat().hasMessageThat().isEqualTo("server is forcefully shut down"); + } + } + @Test public void initialStartIoException() throws Exception { final SettableFuture start = SettableFuture.create(); From 8382bd8e04347478bcf483319fb0b376f726023d Mon Sep 17 00:00:00 2001 From: yifeizhuang Date: Thu, 18 Nov 2021 14:30:35 -0800 Subject: [PATCH 40/50] xds: fix clusterImplLoadBalancer NPE when lrs is null (#8713) --- .../main/java/io/grpc/xds/ClusterImplLoadBalancer.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/xds/src/main/java/io/grpc/xds/ClusterImplLoadBalancer.java b/xds/src/main/java/io/grpc/xds/ClusterImplLoadBalancer.java index 60e6c696daf..b4fc017d2d1 100644 --- a/xds/src/main/java/io/grpc/xds/ClusterImplLoadBalancer.java +++ b/xds/src/main/java/io/grpc/xds/ClusterImplLoadBalancer.java @@ -319,9 +319,11 @@ public PickResult pickSubchannel(PickSubchannelArgs args) { } final ClusterLocalityStats stats = result.getSubchannel().getAttributes().get(ATTR_CLUSTER_LOCALITY_STATS); - ClientStreamTracer.Factory tracerFactory = new CountingStreamTracerFactory( - stats, inFlights, result.getStreamTracerFactory()); - return PickResult.withSubchannel(result.getSubchannel(), tracerFactory); + if (stats != null) { + ClientStreamTracer.Factory tracerFactory = new CountingStreamTracerFactory( + stats, inFlights, result.getStreamTracerFactory()); + return PickResult.withSubchannel(result.getSubchannel(), tracerFactory); + } } return result; } From a5f1fb51b81bce57bc0dc286501e9887f8e7c681 Mon Sep 17 00:00:00 2001 From: ZHANG Dapeng Date: Fri, 19 Nov 2021 10:12:39 -0800 Subject: [PATCH 41/50] core: have JsonUtil support parsing String value as number (#8711) As documented in https://developers.google.com/protocol-buffers/docs/proto3#json, the canonical proto-to-json converter converts int64 (Java long) values to string values in Json rather than Json numbers (Java Double). Conversely, either Json string value or number value are accepted to be converted to int64 proto value. To better support service configs defined by protobuf messages, support parsing String values as numbers in `JsonUtil`. --- .../io/grpc/internal/DnsNameResolver.java | 2 +- .../main/java/io/grpc/internal/JsonUtil.java | 81 ++++++++--- .../io/grpc/internal/ServiceConfigUtil.java | 6 +- .../java/io/grpc/internal/JsonUtilTest.java | 133 ++++++++++++++++++ 4 files changed, 195 insertions(+), 27 deletions(-) create mode 100644 core/src/test/java/io/grpc/internal/JsonUtilTest.java diff --git a/core/src/main/java/io/grpc/internal/DnsNameResolver.java b/core/src/main/java/io/grpc/internal/DnsNameResolver.java index bc994a01035..5418a0bd32d 100644 --- a/core/src/main/java/io/grpc/internal/DnsNameResolver.java +++ b/core/src/main/java/io/grpc/internal/DnsNameResolver.java @@ -434,7 +434,7 @@ final int getPort() { @Nullable private static final Double getPercentageFromChoice(Map serviceConfigChoice) { - return JsonUtil.getNumber(serviceConfigChoice, SERVICE_CONFIG_CHOICE_PERCENTAGE_KEY); + return JsonUtil.getNumberAsDouble(serviceConfigChoice, SERVICE_CONFIG_CHOICE_PERCENTAGE_KEY); } @Nullable diff --git a/core/src/main/java/io/grpc/internal/JsonUtil.java b/core/src/main/java/io/grpc/internal/JsonUtil.java index d80b4ed44c4..117135fe634 100644 --- a/core/src/main/java/io/grpc/internal/JsonUtil.java +++ b/core/src/main/java/io/grpc/internal/JsonUtil.java @@ -96,57 +96,92 @@ public static List getListOfStrings(Map obj, String key) { /** * Gets a number from an object for the given key. If the key is not present, this returns null. - * If the value is not a Double, throws an exception. + * If the value does not represent a double, throws an exception. */ @Nullable - public static Double getNumber(Map obj, String key) { + public static Double getNumberAsDouble(Map obj, String key) { assert key != null; if (!obj.containsKey(key)) { return null; } Object value = obj.get(key); - if (!(value instanceof Double)) { - throw new ClassCastException( - String.format("value '%s' for key '%s' in '%s' is not Double", value, key, obj)); + if (value instanceof Double) { + return (Double) value; + } + if (value instanceof String) { + try { + return Double.parseDouble((String) value); + } catch (NumberFormatException e) { + throw new IllegalArgumentException( + String.format("value '%s' for key '%s' is not a double", value, key)); + } } - return (Double) value; + throw new IllegalArgumentException( + String.format("value '%s' for key '%s' in '%s' is not a number", value, key, obj)); } /** * Gets a number from an object for the given key, casted to an integer. If the key is not - * present, this returns null. If the value is not a Double or loses precision when cast to an - * integer, throws an exception. + * present, this returns null. If the value does not represent an integer, throws an exception. */ + @Nullable public static Integer getNumberAsInteger(Map obj, String key) { - Double d = getNumber(obj, key); - if (d == null) { + assert key != null; + if (!obj.containsKey(key)) { return null; } - int i = d.intValue(); - if (i != d) { - throw new ClassCastException("Number expected to be integer: " + d); + Object value = obj.get(key); + if (value instanceof Double) { + Double d = (Double) value; + int i = d.intValue(); + if (i != d) { + throw new ClassCastException("Number expected to be integer: " + d); + } + return i; + } + if (value instanceof String) { + try { + return Integer.parseInt((String) value); + } catch (NumberFormatException e) { + throw new IllegalArgumentException( + String.format("value '%s' for key '%s' is not an integer", value, key)); + } } - return i; + throw new IllegalArgumentException( + String.format("value '%s' for key '%s' is not an integer", value, key)); } /** * Gets a number from an object for the given key, casted to an long. If the key is not - * present, this returns null. If the value is not a Double or loses precision when cast to an - * long, throws an exception. + * present, this returns null. If the value does not represent a long integer, throws an + * exception. */ public static Long getNumberAsLong(Map obj, String key) { - Double d = getNumber(obj, key); - if (d == null) { + assert key != null; + if (!obj.containsKey(key)) { return null; } - long l = d.longValue(); - if (l != d) { - throw new ClassCastException("Number expected to be long: " + d); + Object value = obj.get(key); + if (value instanceof Double) { + Double d = (Double) value; + long l = d.longValue(); + if (l != d) { + throw new ClassCastException("Number expected to be long: " + d); + } + return l; + } + if (value instanceof String) { + try { + return Long.parseLong((String) value); + } catch (NumberFormatException e) { + throw new IllegalArgumentException( + String.format("value '%s' for key '%s' is not a long integer", value, key)); + } } - return l; + throw new IllegalArgumentException( + String.format("value '%s' for key '%s' is not a long integer", value, key)); } - /** * Gets a string from an object for the given key. If the key is not present, this returns null. * If the value is not a String, throws an exception. diff --git a/core/src/main/java/io/grpc/internal/ServiceConfigUtil.java b/core/src/main/java/io/grpc/internal/ServiceConfigUtil.java index 294e9dbcf73..34d44f7f549 100644 --- a/core/src/main/java/io/grpc/internal/ServiceConfigUtil.java +++ b/core/src/main/java/io/grpc/internal/ServiceConfigUtil.java @@ -113,8 +113,8 @@ static Throttle getThrottlePolicy(@Nullable Map serviceConfig) { } // TODO(dapengzhang0): check if this is null. - float maxTokens = JsonUtil.getNumber(throttling, "maxTokens").floatValue(); - float tokenRatio = JsonUtil.getNumber(throttling, "tokenRatio").floatValue(); + float maxTokens = JsonUtil.getNumberAsDouble(throttling, "maxTokens").floatValue(); + float tokenRatio = JsonUtil.getNumberAsDouble(throttling, "tokenRatio").floatValue(); checkState(maxTokens > 0f, "maxToken should be greater than zero"); checkState(tokenRatio > 0f, "tokenRatio should be greater than zero"); return new Throttle(maxTokens, tokenRatio); @@ -137,7 +137,7 @@ static Long getMaxBackoffNanosFromRetryPolicy(Map retryPolicy) { @Nullable static Double getBackoffMultiplierFromRetryPolicy(Map retryPolicy) { - return JsonUtil.getNumber(retryPolicy, "backoffMultiplier"); + return JsonUtil.getNumberAsDouble(retryPolicy, "backoffMultiplier"); } @Nullable diff --git a/core/src/test/java/io/grpc/internal/JsonUtilTest.java b/core/src/test/java/io/grpc/internal/JsonUtilTest.java new file mode 100644 index 00000000000..960800c97c0 --- /dev/null +++ b/core/src/test/java/io/grpc/internal/JsonUtilTest.java @@ -0,0 +1,133 @@ +/* + * Copyright 2021, gRPC Authors All rights reserved. + * + * 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.internal; + +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.fail; + +import java.util.HashMap; +import java.util.Map; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Tests for {@link JsonUtil}. */ +@RunWith(JUnit4.class) +public class JsonUtilTest { + @Test + public void getNumber() { + Map map = new HashMap<>(); + map.put("key_number_1", 1D); + map.put("key_string_2.0", "2.0"); + map.put("key_string_3", "3"); + map.put("key_string_nan", "NaN"); + map.put("key_number_5.5", 5.5D); + map.put("key_string_six", "six"); + map.put("key_string_infinity", "Infinity"); + map.put("key_string_minus_infinity", "-Infinity"); + map.put("key_string_exponent", "2.998e8"); + map.put("key_string_minus_zero", "-0"); + + assertThat(JsonUtil.getNumberAsDouble(map, "key_number_1")).isEqualTo(1D); + assertThat(JsonUtil.getNumberAsInteger(map, "key_number_1")).isEqualTo(1); + assertThat(JsonUtil.getNumberAsLong(map, "key_number_1")).isEqualTo(1L); + + assertThat(JsonUtil.getNumberAsDouble(map, "key_string_2.0")).isEqualTo(2D); + try { + JsonUtil.getNumberAsInteger(map, "key_string_2.0"); + fail("expecting to throw but did not"); + } catch (RuntimeException e) { + assertThat(e).hasMessageThat().isEqualTo( + "value '2.0' for key 'key_string_2.0' is not an integer"); + } + try { + JsonUtil.getNumberAsLong(map, "key_string_2.0"); + fail("expecting to throw but did not"); + } catch (RuntimeException e) { + assertThat(e).hasMessageThat().isEqualTo( + "value '2.0' for key 'key_string_2.0' is not a long integer"); + } + + assertThat(JsonUtil.getNumberAsDouble(map, "key_string_3")).isEqualTo(3D); + assertThat(JsonUtil.getNumberAsInteger(map, "key_string_3")).isEqualTo(3); + assertThat(JsonUtil.getNumberAsLong(map, "key_string_3")).isEqualTo(3L); + + assertThat(JsonUtil.getNumberAsDouble(map, "key_string_nan")).isNaN(); + try { + JsonUtil.getNumberAsInteger(map, "key_string_nan"); + fail("expecting to throw but did not"); + } catch (RuntimeException e) { + assertThat(e).hasMessageThat().isEqualTo( + "value 'NaN' for key 'key_string_nan' is not an integer"); + } + try { + JsonUtil.getNumberAsLong(map, "key_string_nan"); + fail("expecting to throw but did not"); + } catch (RuntimeException e) { + assertThat(e).hasMessageThat().isEqualTo( + "value 'NaN' for key 'key_string_nan' is not a long integer"); + } + + assertThat(JsonUtil.getNumberAsDouble(map, "key_number_5.5")).isEqualTo(5.5D); + try { + JsonUtil.getNumberAsInteger(map, "key_number_5.5"); + fail("expecting to throw but did not"); + } catch (RuntimeException e) { + assertThat(e).hasMessageThat().isEqualTo("Number expected to be integer: 5.5"); + } + try { + JsonUtil.getNumberAsLong(map, "key_number_5.5"); + fail("expecting to throw but did not"); + } catch (RuntimeException e) { + assertThat(e).hasMessageThat().isEqualTo("Number expected to be long: 5.5"); + } + + try { + JsonUtil.getNumberAsDouble(map, "key_string_six"); + fail("expecting to throw but did not"); + } catch (RuntimeException e) { + assertThat(e).hasMessageThat().isEqualTo( + "value 'six' for key 'key_string_six' is not a double"); + } + try { + JsonUtil.getNumberAsInteger(map, "key_string_six"); + fail("expecting to throw but did not"); + } catch (RuntimeException e) { + assertThat(e).hasMessageThat().isEqualTo( + "value 'six' for key 'key_string_six' is not an integer"); + } + try { + JsonUtil.getNumberAsLong(map, "key_string_six"); + fail("expecting to throw but did not"); + } catch (RuntimeException e) { + assertThat(e).hasMessageThat().isEqualTo( + "value 'six' for key 'key_string_six' is not a long integer"); + } + + assertThat(JsonUtil.getNumberAsDouble(map, "key_string_infinity")).isPositiveInfinity(); + assertThat(JsonUtil.getNumberAsDouble(map, "key_string_minus_infinity")).isNegativeInfinity(); + assertThat(JsonUtil.getNumberAsDouble(map, "key_string_exponent")).isEqualTo(2.998e8D); + + assertThat(JsonUtil.getNumberAsDouble(map, "key_string_minus_zero")).isZero(); + assertThat(JsonUtil.getNumberAsInteger(map, "key_string_minus_zero")).isEqualTo(0); + assertThat(JsonUtil.getNumberAsLong(map, "key_string_minus_zero")).isEqualTo(0L); + + assertThat(JsonUtil.getNumberAsDouble(map, "key_nonexistent")).isNull(); + assertThat(JsonUtil.getNumberAsInteger(map, "key_nonexistent")).isNull(); + assertThat(JsonUtil.getNumberAsLong(map, "key_nonexistent")).isNull(); + } +} From 5f3a5f8b375367571c0660e0b9e46c38a4175b6e Mon Sep 17 00:00:00 2001 From: ZHANG Dapeng Date: Mon, 22 Nov 2021 09:02:35 -0800 Subject: [PATCH 42/50] xds: support xdstp scheme in resource URIs for federation (#8716) Implement applying `server_listener_resource_name_template` and `client_listener_resource_name_template` with xdstp scheme, extracting authorities from xdstp resource URI and lookup authorities map in bootstrap. --- .../main/java/io/grpc/xds/Bootstrapper.java | 9 +- .../java/io/grpc/xds/BootstrapperImpl.java | 2 +- .../java/io/grpc/xds/ClientXdsClient.java | 21 +++- .../java/io/grpc/xds/XdsNameResolver.java | 62 ++++++++-- .../io/grpc/xds/XdsNameResolverProvider.java | 7 +- .../java/io/grpc/xds/XdsServerWrapper.java | 8 +- .../io/grpc/xds/ClientXdsClientTestBase.java | 75 +++++++++++- .../java/io/grpc/xds/XdsNameResolverTest.java | 113 ++++++++++++++++-- .../io/grpc/xds/XdsServerWrapperTest.java | 32 +++++ 9 files changed, 295 insertions(+), 34 deletions(-) diff --git a/xds/src/main/java/io/grpc/xds/Bootstrapper.java b/xds/src/main/java/io/grpc/xds/Bootstrapper.java index 862f8691080..57cdc6f324b 100644 --- a/xds/src/main/java/io/grpc/xds/Bootstrapper.java +++ b/xds/src/main/java/io/grpc/xds/Bootstrapper.java @@ -16,6 +16,8 @@ package io.grpc.xds; +import static com.google.common.base.Preconditions.checkArgument; + import com.google.auto.value.AutoValue; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableList; @@ -33,6 +35,8 @@ @Internal public abstract class Bootstrapper { + static final String XDSTP_SCHEME = "xdstp:"; + /** * Returns system-loaded bootstrap configuration. */ @@ -104,12 +108,13 @@ abstract static class AuthorityInfo { *

    If the same server is listed in multiple authorities, the entries will be de-duped (i.e., * resources for both authorities will be fetched on the same ADS stream). * - *

    If empty, the top-level server list {@link BootstrapInfo#servers()} will be used. + *

    Defaults to the top-level server list {@link BootstrapInfo#servers()}. Must not be empty. */ abstract ImmutableList xdsServers(); static AuthorityInfo create( String clientListenerResourceNameTemplate, List xdsServers) { + checkArgument(!xdsServers.isEmpty(), "xdsServers must not be empty"); return new AutoValue_Bootstrapper_AuthorityInfo( clientListenerResourceNameTemplate, ImmutableList.copyOf(xdsServers)); } @@ -121,7 +126,7 @@ static AuthorityInfo create( @AutoValue @Internal public abstract static class BootstrapInfo { - /** Returns the list of xDS servers to be connected to. */ + /** Returns the list of xDS servers to be connected to. Must not be empty. */ abstract ImmutableList servers(); /** Returns the node identifier to be included in xDS requests. */ diff --git a/xds/src/main/java/io/grpc/xds/BootstrapperImpl.java b/xds/src/main/java/io/grpc/xds/BootstrapperImpl.java index 35ff5e55a42..23b14357096 100644 --- a/xds/src/main/java/io/grpc/xds/BootstrapperImpl.java +++ b/xds/src/main/java/io/grpc/xds/BootstrapperImpl.java @@ -229,7 +229,7 @@ BootstrapInfo bootstrap(Map rawData) throws XdsInitializationExceptio JsonUtil.getString(rawAuthority, "client_listener_resource_name_template"); logger.log( XdsLogLevel.INFO, "client_listener_resource_name_template: {0}", clientListnerTemplate); - String prefix = "xdstp://" + authorityName + "/"; + String prefix = XDSTP_SCHEME + "//" + authorityName + "/"; if (clientListnerTemplate == null) { clientListnerTemplate = prefix + "envoy.config.listener.v3.Listener/%s"; } else if (!clientListnerTemplate.startsWith(prefix)) { diff --git a/xds/src/main/java/io/grpc/xds/ClientXdsClient.java b/xds/src/main/java/io/grpc/xds/ClientXdsClient.java index 0a11ad47288..2b676553838 100644 --- a/xds/src/main/java/io/grpc/xds/ClientXdsClient.java +++ b/xds/src/main/java/io/grpc/xds/ClientXdsClient.java @@ -18,6 +18,7 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; +import static io.grpc.xds.Bootstrapper.XDSTP_SCHEME; import com.github.udpa.udpa.type.v1.TypedStruct; import com.google.common.annotations.VisibleForTesting; @@ -70,6 +71,7 @@ import io.grpc.internal.BackoffPolicy; import io.grpc.internal.TimeProvider; import io.grpc.xds.AbstractXdsClient.ResourceType; +import io.grpc.xds.Bootstrapper.AuthorityInfo; import io.grpc.xds.Bootstrapper.ServerInfo; import io.grpc.xds.Endpoints.DropOverload; import io.grpc.xds.Endpoints.LbEndpoint; @@ -98,6 +100,7 @@ import io.grpc.xds.internal.Matchers.FractionMatcher; import io.grpc.xds.internal.Matchers.HeaderMatcher; import java.net.InetSocketAddress; +import java.net.URI; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Arrays; @@ -2267,8 +2270,12 @@ private final class ResourceSubscriber { ResourceSubscriber(ResourceType type, String resource) { syncContext.throwIfNotInThisSynchronizationContext(); this.type = type; + // TODO(zdapeng): Validate authority in resource URI for new-style resource name + // when parsing XDS response. + // TODO(zdapeng): Canonicalize the resource name by sorting the context params in normal + // lexicographic order. this.resource = resource; - this.serverInfo = getServerInfo(); + this.serverInfo = getServerInfo(resource); // Initialize metadata in UNKNOWN state to cover the case when resource subscriber, // is created but not yet requested because the client is in backoff. this.metadata = ResourceMetadata.newResourceMetadataUnknown(); @@ -2280,8 +2287,16 @@ private final class ResourceSubscriber { restartTimer(); } - // TODO(zdapeng): add resourceName arg and support xdstp:// resources - private ServerInfo getServerInfo() { + private ServerInfo getServerInfo(String resource) { + if (resource.startsWith(XDSTP_SCHEME)) { + URI uri = URI.create(resource); + String authority = uri.getAuthority(); + if (authority == null) { + authority = ""; + } + AuthorityInfo authorityInfo = bootstrapInfo.authorities().get(authority); + return authorityInfo.xdsServers().get(0); + } return bootstrapInfo.servers().get(0); // use first server } diff --git a/xds/src/main/java/io/grpc/xds/XdsNameResolver.java b/xds/src/main/java/io/grpc/xds/XdsNameResolver.java index 4cd52c8b3f9..b6b66327525 100644 --- a/xds/src/main/java/io/grpc/xds/XdsNameResolver.java +++ b/xds/src/main/java/io/grpc/xds/XdsNameResolver.java @@ -18,12 +18,14 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; +import static io.grpc.xds.Bootstrapper.XDSTP_SCHEME; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Joiner; import com.google.common.base.Strings; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Sets; +import com.google.common.net.UrlEscapers; import com.google.gson.Gson; import com.google.protobuf.util.Durations; import io.grpc.Attributes; @@ -45,6 +47,8 @@ import io.grpc.SynchronizationContext; import io.grpc.internal.GrpcUtil; import io.grpc.internal.ObjectPool; +import io.grpc.xds.Bootstrapper.AuthorityInfo; +import io.grpc.xds.Bootstrapper.BootstrapInfo; import io.grpc.xds.Filter.ClientInterceptorBuilder; import io.grpc.xds.Filter.FilterConfig; import io.grpc.xds.Filter.NamedFilterConfig; @@ -101,7 +105,9 @@ final class XdsNameResolver extends NameResolver { private final InternalLogId logId; private final XdsLogger logger; - private final String authority; + @Nullable + private final String targetAuthority; + private final String serviceAuthority; private final ServiceConfigParser serviceConfigParser; private final SynchronizationContext syncContext; private final ScheduledExecutorService scheduler; @@ -120,20 +126,23 @@ final class XdsNameResolver extends NameResolver { private CallCounterProvider callCounterProvider; private ResolveState resolveState; - XdsNameResolver(String name, ServiceConfigParser serviceConfigParser, + XdsNameResolver( + @Nullable String targetAuthority, String name, ServiceConfigParser serviceConfigParser, SynchronizationContext syncContext, ScheduledExecutorService scheduler, @Nullable Map bootstrapOverride) { - this(name, serviceConfigParser, syncContext, scheduler, + this(targetAuthority, name, serviceConfigParser, syncContext, scheduler, SharedXdsClientPoolProvider.getDefaultProvider(), ThreadSafeRandomImpl.instance, FilterRegistry.getDefaultRegistry(), bootstrapOverride); } @VisibleForTesting - XdsNameResolver(String name, ServiceConfigParser serviceConfigParser, + XdsNameResolver( + @Nullable String targetAuthority, String name, ServiceConfigParser serviceConfigParser, SynchronizationContext syncContext, ScheduledExecutorService scheduler, XdsClientPoolFactory xdsClientPoolFactory, ThreadSafeRandom random, FilterRegistry filterRegistry, @Nullable Map bootstrapOverride) { - authority = GrpcUtil.checkAuthority(checkNotNull(name, "name")); + this.targetAuthority = targetAuthority; + serviceAuthority = GrpcUtil.checkAuthority(checkNotNull(name, "name")); this.serviceConfigParser = checkNotNull(serviceConfigParser, "serviceConfigParser"); this.syncContext = checkNotNull(syncContext, "syncContext"); this.scheduler = checkNotNull(scheduler, "scheduler"); @@ -149,7 +158,7 @@ final class XdsNameResolver extends NameResolver { @Override public String getServiceAuthority() { - return authority; + return serviceAuthority; } @Override @@ -163,11 +172,33 @@ public void start(Listener2 listener) { return; } xdsClient = xdsClientPool.getObject(); + BootstrapInfo bootstrapInfo = xdsClient.getBootstrapInfo(); + String listenerNameTemplate; + if (targetAuthority == null) { + listenerNameTemplate = bootstrapInfo.clientDefaultListenerResourceNameTemplate(); + } else { + AuthorityInfo authorityInfo = bootstrapInfo.authorities().get(targetAuthority); + if (authorityInfo == null) { + listener.onError(Status.INVALID_ARGUMENT.withDescription( + "invalid target URI: target authority not found in the bootstrap")); + return; + } + listenerNameTemplate = authorityInfo.clientListenerResourceNameTemplate(); + } + String replacement = serviceAuthority; + if (listenerNameTemplate.startsWith(XDSTP_SCHEME)) { + replacement = UrlEscapers.urlFragmentEscaper().escape(replacement); + } + String ldsResourceName = expandPercentS(listenerNameTemplate, replacement); callCounterProvider = SharedCallCounterMap.getInstance(); - resolveState = new ResolveState(); + resolveState = new ResolveState(ldsResourceName); resolveState.start(); } + private static String expandPercentS(String template, String replacement) { + return template.replace("%s", replacement); + } + @Override public void shutdown() { logger.log(XdsLogLevel.INFO, "Shutdown"); @@ -624,12 +655,17 @@ private class ResolveState implements LdsResourceWatcher { .setServiceConfig(emptyServiceConfig) // let channel take action for no config selector .build(); + private final String ldsResourceName; private boolean stopped; @Nullable private Set existingClusters; // clusters to which new requests can be routed @Nullable private RouteDiscoveryState routeDiscoveryState; + ResolveState(String ldsResourceName) { + this.ldsResourceName = ldsResourceName; + } + @Override public void onChanged(final LdsUpdate update) { syncContext.execute(new Runnable() { @@ -686,23 +722,23 @@ public void run() { } private void start() { - logger.log(XdsLogLevel.INFO, "Start watching LDS resource {0}", authority); - xdsClient.watchLdsResource(authority, this); + logger.log(XdsLogLevel.INFO, "Start watching LDS resource {0}", ldsResourceName); + xdsClient.watchLdsResource(ldsResourceName, this); } private void stop() { - logger.log(XdsLogLevel.INFO, "Stop watching LDS resource {0}", authority); + logger.log(XdsLogLevel.INFO, "Stop watching LDS resource {0}", ldsResourceName); stopped = true; cleanUpRouteDiscoveryState(); - xdsClient.cancelLdsResourceWatch(authority, this); + xdsClient.cancelLdsResourceWatch(ldsResourceName, this); } private void updateRoutes(List virtualHosts, long httpMaxStreamDurationNano, @Nullable List filterConfigs) { - VirtualHost virtualHost = findVirtualHostForHostName(virtualHosts, authority); + VirtualHost virtualHost = findVirtualHostForHostName(virtualHosts, ldsResourceName); if (virtualHost == null) { logger.log(XdsLogLevel.WARNING, - "Failed to find virtual host matching hostname {0}", authority); + "Failed to find virtual host matching hostname {0}", ldsResourceName); cleanUpRoutes(); return; } diff --git a/xds/src/main/java/io/grpc/xds/XdsNameResolverProvider.java b/xds/src/main/java/io/grpc/xds/XdsNameResolverProvider.java index 03d88a9752e..a02e27c37c7 100644 --- a/xds/src/main/java/io/grpc/xds/XdsNameResolverProvider.java +++ b/xds/src/main/java/io/grpc/xds/XdsNameResolverProvider.java @@ -74,9 +74,10 @@ public XdsNameResolver newNameResolver(URI targetUri, Args args) { targetPath, targetUri); String name = targetPath.substring(1); - return new XdsNameResolver(name, args.getServiceConfigParser(), - args.getSynchronizationContext(), args.getScheduledExecutorService(), - bootstrapOverride); + return new XdsNameResolver( + targetUri.getAuthority(), name, args.getServiceConfigParser(), + args.getSynchronizationContext(), args.getScheduledExecutorService(), + bootstrapOverride); } return null; } diff --git a/xds/src/main/java/io/grpc/xds/XdsServerWrapper.java b/xds/src/main/java/io/grpc/xds/XdsServerWrapper.java index 6eefcf63411..0946f621487 100644 --- a/xds/src/main/java/io/grpc/xds/XdsServerWrapper.java +++ b/xds/src/main/java/io/grpc/xds/XdsServerWrapper.java @@ -18,11 +18,13 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; +import static io.grpc.xds.Bootstrapper.XDSTP_SCHEME; import com.google.auto.value.AutoValue; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import com.google.common.net.UrlEscapers; import com.google.common.util.concurrent.SettableFuture; import io.grpc.Attributes; import io.grpc.InternalServerInterceptors; @@ -192,7 +194,11 @@ private void internalStart() { xdsClient = xdsClientPool.returnObject(xdsClient); return; } - discoveryState = new DiscoveryState(listenerTemplate.replaceAll("%s", listenerAddress)); + String replacement = listenerAddress; + if (listenerTemplate.startsWith(XDSTP_SCHEME)) { + replacement = UrlEscapers.urlFragmentEscaper().escape(replacement); + } + discoveryState = new DiscoveryState(listenerTemplate.replaceAll("%s", replacement)); } @Override diff --git a/xds/src/test/java/io/grpc/xds/ClientXdsClientTestBase.java b/xds/src/test/java/io/grpc/xds/ClientXdsClientTestBase.java index 9d0c735ba95..6fdf5e1b811 100644 --- a/xds/src/test/java/io/grpc/xds/ClientXdsClientTestBase.java +++ b/xds/src/test/java/io/grpc/xds/ClientXdsClientTestBase.java @@ -59,6 +59,7 @@ import io.grpc.internal.TimeProvider; import io.grpc.testing.GrpcCleanupRule; import io.grpc.xds.AbstractXdsClient.ResourceType; +import io.grpc.xds.Bootstrapper.AuthorityInfo; import io.grpc.xds.Bootstrapper.CertificateProviderInfo; import io.grpc.xds.Bootstrapper.ServerInfo; import io.grpc.xds.ClientXdsClient.XdsChannelFactory; @@ -114,6 +115,8 @@ @RunWith(JUnit4.class) public abstract class ClientXdsClientTestBase { private static final String SERVER_URI = "trafficdirector.googleapis.com"; + private static final String SERVER_URI_CUSTOME_AUTHORITY = "trafficdirector2.googleapis.com"; + private static final String SERVER_URI_EMPTY_AUTHORITY = "trafficdirector3.googleapis.com"; private static final String LDS_RESOURCE = "listener.googleapis.com"; private static final String RDS_RESOURCE = "route-configuration.googleapis.com"; private static final String CDS_RESOURCE = "cluster.googleapis.com"; @@ -250,6 +253,8 @@ public long currentTimeNanos() { private TlsContextManager tlsContextManager; private ManagedChannel channel; + private ManagedChannel channelForCustomAuthority; + private ManagedChannel channelForEmptyAuthority; private ClientXdsClient xdsClient; private boolean originalEnableFaultInjection; private boolean originalEnableRbac; @@ -281,7 +286,24 @@ public void setUp() throws IOException { XdsChannelFactory xdsChannelFactory = new XdsChannelFactory() { @Override ManagedChannel create(ServerInfo serverInfo) { - return channel; + if (serverInfo.target().equals(SERVER_URI)) { + return channel; + } + if (serverInfo.target().equals(SERVER_URI_CUSTOME_AUTHORITY)) { + if (channelForCustomAuthority == null) { + channelForCustomAuthority = cleanupRule.register( + InProcessChannelBuilder.forName(serverName).directExecutor().build()); + } + return channelForCustomAuthority; + } + if (serverInfo.target().equals(SERVER_URI_EMPTY_AUTHORITY)) { + if (channelForEmptyAuthority == null) { + channelForEmptyAuthority = cleanupRule.register( + InProcessChannelBuilder.forName(serverName).directExecutor().build()); + } + return channelForEmptyAuthority; + } + throw new IllegalArgumentException("Can not create channel for " + serverInfo); } }; @@ -290,6 +312,17 @@ ManagedChannel create(ServerInfo serverInfo) { .servers(Arrays.asList( Bootstrapper.ServerInfo.create(SERVER_URI, CHANNEL_CREDENTIALS, useProtocolV3()))) .node(EnvoyProtoData.Node.newBuilder().build()) + .authorities(ImmutableMap.of( + "authority.xds.com", + AuthorityInfo.create( + "xdstp://authority.xds.com/envoy.config.listener.v3.Listener/%s", + ImmutableList.of(Bootstrapper.ServerInfo.create( + SERVER_URI_CUSTOME_AUTHORITY, CHANNEL_CREDENTIALS, useProtocolV3()))), + "", + AuthorityInfo.create( + "xdstp:///envoy.config.listener.v3.Listener/%s", + ImmutableList.of(Bootstrapper.ServerInfo.create( + SERVER_URI_EMPTY_AUTHORITY, CHANNEL_CREDENTIALS, useProtocolV3()))))) .certProviders(ImmutableMap.of("cert-instance-name", CertificateProviderInfo.create("file-watcher", ImmutableMap.of()))) .build(); @@ -706,6 +739,46 @@ public void ldsResourceUpdated() { .isEqualTo(RDS_RESOURCE); verifyResourceMetadataAcked(LDS, LDS_RESOURCE, testListenerRds, VERSION_2, TIME_INCREMENT * 2); verifySubscribedResourcesMetadataSizes(1, 0, 0, 0); + assertThat(channelForCustomAuthority).isNull(); + assertThat(channelForEmptyAuthority).isNull(); + } + + @Test + public void ldsResourceUpdated_withXdstpResourceName() { + String ldsResourceName = + "xdstp://authority.xds.com/envoy.config.listener.v3.Listener/listener1"; + DiscoveryRpcCall call = startResourceWatcher(LDS, ldsResourceName, ldsResourceWatcher); + assertThat(channelForCustomAuthority).isNotNull(); + verifyResourceMetadataRequested(LDS, ldsResourceName); + + Any testListenerVhosts = Any.pack(mf.buildListenerWithApiListener(ldsResourceName, + mf.buildRouteConfiguration("do not care", mf.buildOpaqueVirtualHosts(VHOST_SIZE)))); + call.sendResponse(LDS, testListenerVhosts, VERSION_1, "0000"); + call.verifyRequest(LDS, ldsResourceName, VERSION_1, "0000", NODE); + verify(ldsResourceWatcher).onChanged(ldsUpdateCaptor.capture()); + assertThat(ldsUpdateCaptor.getValue().httpConnectionManager().virtualHosts()) + .hasSize(VHOST_SIZE); + verifyResourceMetadataAcked( + LDS, ldsResourceName, testListenerVhosts, VERSION_1, TIME_INCREMENT); + } + + @Test + public void ldsResourceUpdated_withXdstpResourceName_withEmptyAuthority() { + String ldsResourceName = + "xdstp:///envoy.config.listener.v3.Listener/listener1"; + DiscoveryRpcCall call = startResourceWatcher(LDS, ldsResourceName, ldsResourceWatcher); + assertThat(channelForEmptyAuthority).isNotNull(); + verifyResourceMetadataRequested(LDS, ldsResourceName); + + Any testListenerVhosts = Any.pack(mf.buildListenerWithApiListener(ldsResourceName, + mf.buildRouteConfiguration("do not care", mf.buildOpaqueVirtualHosts(VHOST_SIZE)))); + call.sendResponse(LDS, testListenerVhosts, VERSION_1, "0000"); + call.verifyRequest(LDS, ldsResourceName, VERSION_1, "0000", NODE); + verify(ldsResourceWatcher).onChanged(ldsUpdateCaptor.capture()); + assertThat(ldsUpdateCaptor.getValue().httpConnectionManager().virtualHosts()) + .hasSize(VHOST_SIZE); + verifyResourceMetadataAcked( + LDS, ldsResourceName, testListenerVhosts, VERSION_1, TIME_INCREMENT); } @Test diff --git a/xds/src/test/java/io/grpc/xds/XdsNameResolverTest.java b/xds/src/test/java/io/grpc/xds/XdsNameResolverTest.java index babaa2b3034..7968b7fb366 100644 --- a/xds/src/test/java/io/grpc/xds/XdsNameResolverTest.java +++ b/xds/src/test/java/io/grpc/xds/XdsNameResolverTest.java @@ -26,6 +26,7 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -45,6 +46,7 @@ import io.grpc.ClientInterceptor; import io.grpc.ClientInterceptors; import io.grpc.Deadline; +import io.grpc.InsecureChannelCredentials; import io.grpc.InternalConfigSelector; import io.grpc.InternalConfigSelector.Result; import io.grpc.Metadata; @@ -67,6 +69,10 @@ import io.grpc.internal.PickSubchannelArgsImpl; import io.grpc.internal.ScParser; import io.grpc.testing.TestMethodDescriptors; +import io.grpc.xds.Bootstrapper.AuthorityInfo; +import io.grpc.xds.Bootstrapper.BootstrapInfo; +import io.grpc.xds.Bootstrapper.ServerInfo; +import io.grpc.xds.EnvoyProtoData.Node; import io.grpc.xds.FaultConfig.FaultAbort; import io.grpc.xds.FaultConfig.FaultDelay; import io.grpc.xds.Filter.FilterConfig; @@ -132,6 +138,12 @@ public ConfigOrError parseServiceConfig(Map rawServiceConfig) { private final CallInfo call1 = new CallInfo("HelloService", "hi"); private final CallInfo call2 = new CallInfo("GreetService", "bye"); private final TestChannel channel = new TestChannel(); + private BootstrapInfo bootstrapInfo = BootstrapInfo.builder() + .servers(ImmutableList.of(ServerInfo.create( + "td.googleapis.com", InsecureChannelCredentials.create(), true))) + .node(Node.newBuilder().build()) + .build(); + private String expectedLdsResourceName = AUTHORITY; @Mock private ThreadSafeRandom mockRandom; @@ -152,7 +164,7 @@ public void setUp() { FilterRegistry filterRegistry = FilterRegistry.newRegistry().register( new FaultFilter(mockRandom, new AtomicLong()), RouterFilter.INSTANCE); - resolver = new XdsNameResolver(AUTHORITY, serviceConfigParser, syncContext, scheduler, + resolver = new XdsNameResolver(null, AUTHORITY, serviceConfigParser, syncContext, scheduler, xdsClientPoolFactory, mockRandom, filterRegistry, null); } @@ -185,7 +197,7 @@ public ObjectPool getOrCreate() throws XdsInitializationException { throw new XdsInitializationException("Fail to read bootstrap file"); } }; - resolver = new XdsNameResolver(AUTHORITY, serviceConfigParser, syncContext, scheduler, + resolver = new XdsNameResolver(null, AUTHORITY, serviceConfigParser, syncContext, scheduler, xdsClientPoolFactory, mockRandom, FilterRegistry.getDefaultRegistry(), null); resolver.start(mockListener); verify(mockListener).onError(errorCaptor.capture()); @@ -195,6 +207,79 @@ public ObjectPool getOrCreate() throws XdsInitializationException { assertThat(error.getCause()).hasMessageThat().isEqualTo("Fail to read bootstrap file"); } + @Test + public void resolving_withTargetAuthorityNotFound() { + resolver = new XdsNameResolver( + "notfound.google.com", AUTHORITY, serviceConfigParser, syncContext, scheduler, + xdsClientPoolFactory, mockRandom, FilterRegistry.getDefaultRegistry(), null); + resolver.start(mockListener); + verify(mockListener).onError(errorCaptor.capture()); + Status error = errorCaptor.getValue(); + assertThat(error.getCode()).isEqualTo(Code.INVALID_ARGUMENT); + assertThat(error.getDescription()).isEqualTo( + "invalid target URI: target authority not found in the bootstrap"); + } + + @Test + public void resolving_noTargetAuthority_templateWithoutXdstp() { + bootstrapInfo = BootstrapInfo.builder() + .servers(ImmutableList.of(ServerInfo.create( + "td.googleapis.com", InsecureChannelCredentials.create(), true))) + .node(Node.newBuilder().build()) + .clientDefaultListenerResourceNameTemplate("%s/id=1") + .build(); + String serviceAuthority = "[::FFFF:129.144.52.38]:80"; + expectedLdsResourceName = "[::FFFF:129.144.52.38]:80/id=1"; + resolver = new XdsNameResolver( + null, serviceAuthority, serviceConfigParser, syncContext, scheduler, xdsClientPoolFactory, + mockRandom, FilterRegistry.getDefaultRegistry(), null); + resolver.start(mockListener); + verify(mockListener, never()).onError(any(Status.class)); + } + + @Test + public void resolving_noTargetAuthority_templateWithXdstp() { + bootstrapInfo = BootstrapInfo.builder() + .servers(ImmutableList.of(ServerInfo.create( + "td.googleapis.com", InsecureChannelCredentials.create(), true))) + .node(Node.newBuilder().build()) + .clientDefaultListenerResourceNameTemplate( + "xdstp://xds.authority.com/envoy.config.listener.v3.Listener/%s?id=1") + .build(); + String serviceAuthority = "[::FFFF:129.144.52.38]:80"; + expectedLdsResourceName = + "xdstp://xds.authority.com/envoy.config.listener.v3.Listener/" + + "%5B::FFFF:129.144.52.38%5D:80?id=1"; + resolver = new XdsNameResolver( + null, serviceAuthority, serviceConfigParser, syncContext, scheduler, + xdsClientPoolFactory, mockRandom, FilterRegistry.getDefaultRegistry(), null); + resolver.start(mockListener); + verify(mockListener, never()).onError(any(Status.class)); + } + + @Test + public void resolving_targetAuthorityInAuthoritiesMap() { + String targetAuthority = "xds.authority.com"; + String serviceAuthority = "[::FFFF:129.144.52.38]:80"; + bootstrapInfo = BootstrapInfo.builder() + .servers(ImmutableList.of(ServerInfo.create( + "td.googleapis.com", InsecureChannelCredentials.create(), true))) + .node(Node.newBuilder().build()) + .authorities( + ImmutableMap.of(targetAuthority, AuthorityInfo.create( + "xdstp://" + targetAuthority + "/envoy.config.listener.v3.Listener/%s", + ImmutableList.of(ServerInfo.create( + "td.googleapis.com", InsecureChannelCredentials.create(), true))))) + .build(); + expectedLdsResourceName = + "xdstp://xds.authority.com/envoy.config.listener.v3.Listener/%5B::FFFF:129.144.52.38%5D:80"; + resolver = new XdsNameResolver( + "xds.authority.com", serviceAuthority, serviceConfigParser, syncContext, scheduler, + xdsClientPoolFactory, mockRandom, FilterRegistry.getDefaultRegistry(), null); + resolver.start(mockListener); + verify(mockListener, never()).onError(any(Status.class)); + } + @Test public void resolving_ldsResourceNotFound() { resolver.start(mockListener); @@ -435,7 +520,7 @@ public void resolved_fallbackToHttpMaxStreamDurationAsTimeout() { public void retryPolicyInPerMethodConfigGeneratedByResolverIsValid() { ServiceConfigParser realParser = new ScParser( true, 5, 5, new AutoConfiguredLoadBalancerFactory("pick-first")); - resolver = new XdsNameResolver(AUTHORITY, realParser, syncContext, scheduler, + resolver = new XdsNameResolver(null, AUTHORITY, realParser, syncContext, scheduler, xdsClientPoolFactory, mockRandom, FilterRegistry.getDefaultRegistry(), null); resolver.start(mockListener); FakeXdsClient xdsClient = (FakeXdsClient) resolver.getXdsClient(); @@ -638,7 +723,7 @@ public void resolved_rpcHashingByChannelId() { // A different resolver/Channel. resolver.shutdown(); reset(mockListener); - resolver = new XdsNameResolver(AUTHORITY, serviceConfigParser, syncContext, scheduler, + resolver = new XdsNameResolver(null, AUTHORITY, serviceConfigParser, syncContext, scheduler, xdsClientPoolFactory, mockRandom, FilterRegistry.getDefaultRegistry(), null); resolver.start(mockListener); xdsClient = (FakeXdsClient) resolver.getXdsClient(); @@ -1698,8 +1783,11 @@ public void routeMatching_withHeaders() { } private final class FakeXdsClientPoolFactory implements XdsClientPoolFactory { + Map bootstrap; + @Override public void setBootstrapOverride(Map bootstrap) { + this.bootstrap = bootstrap; } @Override @@ -1731,11 +1819,16 @@ private class FakeXdsClient extends XdsClient { private LdsResourceWatcher ldsWatcher; private RdsResourceWatcher rdsWatcher; + @Override + BootstrapInfo getBootstrapInfo() { + return bootstrapInfo; + } + @Override void watchLdsResource(String resourceName, LdsResourceWatcher watcher) { assertThat(ldsResource).isNull(); assertThat(ldsWatcher).isNull(); - assertThat(resourceName).isEqualTo(AUTHORITY); + assertThat(resourceName).isEqualTo(expectedLdsResourceName); ldsResource = resourceName; ldsWatcher = watcher; } @@ -1744,7 +1837,7 @@ void watchLdsResource(String resourceName, LdsResourceWatcher watcher) { void cancelLdsResourceWatch(String resourceName, LdsResourceWatcher watcher) { assertThat(ldsResource).isNotNull(); assertThat(ldsWatcher).isNotNull(); - assertThat(resourceName).isEqualTo(AUTHORITY); + assertThat(resourceName).isEqualTo(expectedLdsResourceName); ldsResource = null; ldsWatcher = null; } @@ -1773,7 +1866,7 @@ void deliverLdsUpdate(long httpMaxStreamDurationNano, List virtualH void deliverLdsUpdate(final List routes) { VirtualHost virtualHost = VirtualHost.create( - "virtual-host", Collections.singletonList(AUTHORITY), routes, + "virtual-host", Collections.singletonList(expectedLdsResourceName), routes, ImmutableMap.of()); ldsWatcher.onChanged(LdsUpdate.forApiListener(HttpConnectionManager.forVirtualHosts( 0L, Collections.singletonList(virtualHost), null))); @@ -1817,7 +1910,7 @@ void deliverLdsUpdateWithFaultInjection( FAULT_FILTER_INSTANCE_NAME, virtualHostFaultConfig); VirtualHost virtualHost = VirtualHost.create( "virtual-host", - Collections.singletonList(AUTHORITY), + Collections.singletonList(expectedLdsResourceName), Collections.singletonList(route), overrideConfig); ldsWatcher.onChanged(LdsUpdate.forApiListener(HttpConnectionManager.forVirtualHosts( @@ -1843,7 +1936,7 @@ void deliverLdsUpdateForRdsName(String rdsName) { } void deliverLdsResourceNotFound() { - ldsWatcher.onResourceDoesNotExist(AUTHORITY); + ldsWatcher.onResourceDoesNotExist(expectedLdsResourceName); } void deliverRdsUpdateWithFaultInjection( @@ -1876,7 +1969,7 @@ void deliverRdsUpdateWithFaultInjection( FAULT_FILTER_INSTANCE_NAME, virtualHostFaultConfig); VirtualHost virtualHost = VirtualHost.create( "virtual-host", - Collections.singletonList(AUTHORITY), + Collections.singletonList(expectedLdsResourceName), Collections.singletonList(route), overrideConfig); rdsWatcher.onChanged(new RdsUpdate(Collections.singletonList(virtualHost))); diff --git a/xds/src/test/java/io/grpc/xds/XdsServerWrapperTest.java b/xds/src/test/java/io/grpc/xds/XdsServerWrapperTest.java index bfb16c9745e..f011b789da9 100644 --- a/xds/src/test/java/io/grpc/xds/XdsServerWrapperTest.java +++ b/xds/src/test/java/io/grpc/xds/XdsServerWrapperTest.java @@ -26,6 +26,7 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -55,6 +56,7 @@ import io.grpc.xds.VirtualHost.Route; import io.grpc.xds.VirtualHost.Route.RouteMatch; import io.grpc.xds.VirtualHost.Route.RouteMatch.PathMatcher; +import io.grpc.xds.XdsClient.LdsResourceWatcher; import io.grpc.xds.XdsClient.RdsResourceWatcher; import io.grpc.xds.XdsClient.RdsUpdate; import io.grpc.xds.XdsServerBuilder.XdsServingStatusListener; @@ -173,6 +175,36 @@ public void run() { } } + @Test + public void testBootstrap_templateWithXdstp() throws Exception { + Bootstrapper.BootstrapInfo b = Bootstrapper.BootstrapInfo.builder() + .servers(Arrays.asList( + Bootstrapper.ServerInfo.create( + "uri", InsecureChannelCredentials.create(), true))) + .node(EnvoyProtoData.Node.newBuilder().setId("id").build()) + .serverListenerResourceNameTemplate( + "xdstp://xds.authority.com/envoy.config.listener.v3.Listener/grpc/server/%s") + .build(); + XdsClient xdsClient = mock(XdsClient.class); + when(xdsClient.getBootstrapInfo()).thenReturn(b); + xdsServerWrapper = new XdsServerWrapper("[::FFFF:129.144.52.38]:80", mockBuilder, listener, + selectorManager, new FakeXdsClientPoolFactory(xdsClient), filterRegistry); + Executors.newSingleThreadExecutor().execute(new Runnable() { + @Override + public void run() { + try { + xdsServerWrapper.start(); + } catch (IOException ex) { + // ignore + } + } + }); + verify(xdsClient, timeout(5000)).watchLdsResource( + eq("xdstp://xds.authority.com/envoy.config.listener.v3.Listener/grpc/server/" + + "%5B::FFFF:129.144.52.38%5D:80"), + any(LdsResourceWatcher.class)); + } + @Test public void shutdown() throws Exception { final SettableFuture start = SettableFuture.create(); From ee581bfdfa03e7b0f96fd99e287f5a326648bc42 Mon Sep 17 00:00:00 2001 From: markb74 <57717302+markb74@users.noreply.github.com> Date: Tue, 23 Nov 2021 18:11:54 +0100 Subject: [PATCH 43/50] buildscripts: add config for building grpc-binder artifact (#8722) --- buildscripts/kokoro/linux_artifacts.sh | 9 ++++++++- buildscripts/kokoro/upload_artifacts.sh | 3 +++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/buildscripts/kokoro/linux_artifacts.sh b/buildscripts/kokoro/linux_artifacts.sh index 17402fe89b3..c4386b94e07 100755 --- a/buildscripts/kokoro/linux_artifacts.sh +++ b/buildscripts/kokoro/linux_artifacts.sh @@ -10,7 +10,7 @@ readonly GRPC_JAVA_DIR="$(cd "$(dirname "$0")"/../.. && pwd)" "$GRPC_JAVA_DIR"/buildscripts/build_docker.sh "$GRPC_JAVA_DIR"/buildscripts/run_in_docker.sh /grpc-java/buildscripts/build_artifacts_in_docker.sh -# grpc-android and grpc-cronet require the Android SDK, so build outside of Docker and +# grpc-android, grpc-cronet and grpc-binder require the Android SDK, so build outside of Docker and # use --include-build for its grpc-core dependency echo y | ${ANDROID_HOME}/tools/bin/sdkmanager "build-tools;28.0.3" LOCAL_MVN_TEMP=$(mktemp -d) @@ -28,6 +28,13 @@ pushd "$GRPC_JAVA_DIR/cronet" -PrepositoryDir="$LOCAL_MVN_TEMP" popd +pushd "$GRPC_JAVA_DIR/binder" +../gradlew publish \ + -Dorg.gradle.parallel=false \ + -PskipCodegen=true \ + -PrepositoryDir="$LOCAL_MVN_TEMP" +popd + readonly MVN_ARTIFACT_DIR="${MVN_ARTIFACT_DIR:-$GRPC_JAVA_DIR/mvn-artifacts}" mkdir -p "$MVN_ARTIFACT_DIR" cp -r "$LOCAL_MVN_TEMP"/* "$MVN_ARTIFACT_DIR"/ diff --git a/buildscripts/kokoro/upload_artifacts.sh b/buildscripts/kokoro/upload_artifacts.sh index 20e16c38a2f..06d037831ec 100644 --- a/buildscripts/kokoro/upload_artifacts.sh +++ b/buildscripts/kokoro/upload_artifacts.sh @@ -24,6 +24,9 @@ LOCAL_OTHER_ARTIFACTS="$KOKORO_GFILE_DIR"/github/grpc-java/artifacts/ # cronet artifact from linux job: [[ "$(find "$LOCAL_MVN_ARTIFACTS" -type f -iname 'grpc-cronet-*.aar' | wc -l)" != '0' ]] +# binder artifact from linux job: +[[ "$(find "$LOCAL_MVN_ARTIFACTS" -type f -iname 'grpc-binder-*.aar' | wc -l)" != '0' ]] + # from linux job: [[ "$(find "$LOCAL_MVN_ARTIFACTS" -type f -iname 'protoc-gen-grpc-java-*-linux-x86_64.exe' | wc -l)" != '0' ]] [[ "$(find "$LOCAL_MVN_ARTIFACTS" -type f -iname 'protoc-gen-grpc-java-*-linux-x86_32.exe' | wc -l)" != '0' ]] From a4334eb5c30c40419701835378b357fc0d2bcd27 Mon Sep 17 00:00:00 2001 From: ZHANG Dapeng Date: Mon, 29 Nov 2021 08:48:51 -0800 Subject: [PATCH 44/50] census: fix NPE in calling recordFinishedAttempt() (#8706) Fix the NPE as shown in the following stacktrace: ``` Caused by: java.lang.RuntimeException: java.lang.NullPointerException with message: null at io.grpc.census.CensusStatsModule$ClientTracer.recordFinishedAttempt(CensusStatsModule.java:388) ~[grpc-census-1.42.0.jar:1.42.0] at io.grpc.census.CensusStatsModule$CallAttemptsTracerFactory.recordFinishedCall(CensusStatsModule.java:525) ~[grpc-census-1.42.0.jar:1.42.0] at io.grpc.census.CensusStatsModule$CallAttemptsTracerFactory.attemptEnded(CensusStatsModule.java:492) ~[grpc-census-1.42.0.jar:1.42.0] at io.grpc.census.CensusStatsModule$ClientTracer.streamClosed(CensusStatsModule.java:345) ~[grpc-census-1.42.0.jar:1.42.0] at io.grpc.internal.StatsTraceContext.streamClosed(StatsTraceContext.java:155) ~[grpc-core-1.42.0.jar:1.42.0] at io.grpc.internal.AbstractClientStream$TransportState.closeListener(AbstractClientStream.java:458) ~[grpc-core-1.42.0.jar:1.42.0] at io.grpc.internal.AbstractClientStream$TransportState.access$400(AbstractClientStream.java:221) ~[grpc-core-1.42.0.jar:1.42.0] at io.grpc.internal.AbstractClientStream$TransportState$1.run(AbstractClientStream.java:442) ~[grpc-core-1.42.0.jar:1.42.0] at io.grpc.internal.AbstractClientStream$TransportState.deframerClosed(AbstractClientStream.java:278) ~[grpc-core-1.42.0.jar:1.42.0] at io.grpc.internal.Http2ClientStreamTransportState.deframerClosed(Http2ClientStreamTransportState.java:31) ~[grpc-core-1.42.0.jar:1.42.0] at io.grpc.internal.MessageDeframer.close(MessageDeframer.java:233) ~[grpc-core-1.42.0.jar:1.42.0] at io.grpc.internal.MessageDeframer.closeWhenComplete(MessageDeframer.java:191) ~[grpc-core-1.42.0.jar:1.42.0] at io.grpc.internal.AbstractStream$TransportState.closeDeframer(AbstractStream.java:200) ~[grpc-core-1.42.0.jar:1.42.0] at io.grpc.internal.AbstractClientStream$TransportState.transportReportStatus(AbstractClientStream.java:445) ~[grpc-core-1.42.0.jar:1.42.0] at io.grpc.internal.AbstractClientStream$TransportState.transportReportStatus(AbstractClientStream.java:401) ~[grpc-core-1.42.0.jar:1.42.0] at io.grpc.internal.AbstractClientStream$TransportState.inboundTrailersReceived(AbstractClientStream.java:384) ~[grpc-core-1.42.0.jar:1.42.0] at io.grpc.internal.Http2ClientStreamTransportState.transportTrailersReceived(Http2ClientStreamTransportState.java:183) ~[grpc-core-1.42.0.jar:1.42.0] at io.grpc.netty.shaded.io.grpc.netty.NettyClientStream$TransportState.transportHeadersReceived(NettyClientStream.java:334) ~[grpc-netty-shaded-1.42.0.jar:1.42.0] at io.grpc.netty.shaded.io.grpc.netty.NettyClientHandler.onHeadersRead(NettyClientHandler.java:372) ~[grpc-netty-shaded-1.42.0.jar:1.42.0] at io.grpc.netty.shaded.io.grpc.netty.NettyClientHandler.access$1200(NettyClientHandler.java:91) ~[grpc-netty-shaded-1.42.0.jar:1.42.0] at io.grpc.netty.shaded.io.grpc.netty.NettyClientHandler$FrameListener.onHeadersRead(NettyClientHandler.java:934) ~[grpc-netty-shaded-1.42.0.jar:1.42.0] ``` The NPE can happen when `ClientCall.Listener.onClose()` and `StatsTraceContext.streamClosed()` (or `ClientStreamListener.closed()`) are invoked concurrently in different threads. Note that `CensusStatsModule$CallAttemptsTracerFactory.attemptEnded()` in the above stack trace would observe `callEnded==true` in such a race condition. The following are the possible scenarios that the race between `ClientCall.Listener.onClose()` and `ClientStreamListener.closed()` can happen: - Deadline exceeded but the underlying real stream is [not committed](https://github.com/grpc/grpc-java/blob/v1.42.0/core/src/main/java/io/grpc/internal/RetriableStream.java#L486-L495), the `ClientCall.Listener` may be closed earlier than the stream listener. (This is the case of the above stack trace, in which the inbound end-of-stream is received observing `callEnded==true`. Even if nothing inbound is received, there is still a chance that the NPE can happen.) - DelayedClientTransport.PendingStream has created a realStream but `setStream(realStream)` is [not called yet](https://github.com/grpc/grpc-java/blob/v1.42.0/core/src/main/java/io/grpc/internal/DelayedClientTransport.java#L366-L372), when deadline exceeded. (This has little chance to happen, only for the very first RPC on the channel.) - Hedging case. In deadline-exceeded cases, the shorter the deadline is, the more likely the race can happen. --- census/src/main/java/io/grpc/census/CensusStatsModule.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/census/src/main/java/io/grpc/census/CensusStatsModule.java b/census/src/main/java/io/grpc/census/CensusStatsModule.java index 4b2832c5989..487e49e5005 100644 --- a/census/src/main/java/io/grpc/census/CensusStatsModule.java +++ b/census/src/main/java/io/grpc/census/CensusStatsModule.java @@ -342,7 +342,6 @@ public void outboundMessage(int seqNo) { @Override public void streamClosed(Status status) { - attemptsState.attemptEnded(); stopwatch.stop(); roundtripNanos = stopwatch.elapsed(TimeUnit.NANOSECONDS); Deadline deadline = info.getCallOptions().getDeadline(); @@ -355,6 +354,7 @@ public void streamClosed(Status status) { statusCode = Code.DEADLINE_EXCEEDED; } } + attemptsState.attemptEnded(); if (inboundReceivedOrClosed.compareAndSet(false, true)) { if (module.recordFinishedRpcs) { // Stream is closed early. So no need to record metrics for any inbound events after this @@ -522,6 +522,8 @@ void recordFinishedCall() { tracer.statusCode = status.getCode(); tracer.recordFinishedAttempt(); } else if (inboundMetricTracer != null) { + // activeStreams has been decremented to 0 by attemptEnded(), + // so inboundMetricTracer.statusCode is guaranteed to be assigned already. inboundMetricTracer.recordFinishedAttempt(); } if (!module.recordRetryMetrics) { From cb4b91418c34364b91ece9e9ca6fb5845abad1bf Mon Sep 17 00:00:00 2001 From: ZHANG Dapeng Date: Mon, 29 Nov 2021 10:22:32 -0800 Subject: [PATCH 45/50] javadoc: update new API `@since` version (#8727) The @since version in commits 5a3b8e2 and a2398ce were missing and incorrect respectively. --- .../java/io/grpc/alts/GoogleDefaultChannelCredentials.java | 4 ++-- okhttp/src/main/java/io/grpc/okhttp/OkHttpChannelBuilder.java | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/alts/src/main/java/io/grpc/alts/GoogleDefaultChannelCredentials.java b/alts/src/main/java/io/grpc/alts/GoogleDefaultChannelCredentials.java index fe8c005da45..d9c2ddaaed7 100644 --- a/alts/src/main/java/io/grpc/alts/GoogleDefaultChannelCredentials.java +++ b/alts/src/main/java/io/grpc/alts/GoogleDefaultChannelCredentials.java @@ -50,7 +50,7 @@ public static ChannelCredentials create() { /** * Returns a new instance of {@link Builder}. * - * @since 1.42.0 + * @since 1.43.0 */ public static Builder newBuilder() { return new Builder(); @@ -59,7 +59,7 @@ public static Builder newBuilder() { /** * Builder for {@link GoogleDefaultChannelCredentials} instances. * - * @since 1.42.0 + * @since 1.43.0 */ public static final class Builder { private CallCredentials callCredentials; diff --git a/okhttp/src/main/java/io/grpc/okhttp/OkHttpChannelBuilder.java b/okhttp/src/main/java/io/grpc/okhttp/OkHttpChannelBuilder.java index b3e90d158fa..68ff6dbd787 100644 --- a/okhttp/src/main/java/io/grpc/okhttp/OkHttpChannelBuilder.java +++ b/okhttp/src/main/java/io/grpc/okhttp/OkHttpChannelBuilder.java @@ -413,6 +413,8 @@ public OkHttpChannelBuilder connectionSpec( * * @param tlsVersions List of tls versions. * @param cipherSuites List of cipher suites. + * + * @since 1.43.0 */ public OkHttpChannelBuilder tlsConnectionSpec( String[] tlsVersions, String[] cipherSuites) { From 2330922c3809b978f8eb08d14501553ed7f6fb5b Mon Sep 17 00:00:00 2001 From: ZHANG Dapeng Date: Tue, 30 Nov 2021 08:36:20 -0800 Subject: [PATCH 46/50] rls: overhaul RouteLookupConfig validation (#8645) The `RlsProtoData.RouteLookupConfig` class is out-of-date. - Some of the fields were long, but now are of `Duration` type. - Some of the fields are deleted. - The validation of some of the fields either have been changed or were wrong since beginning. Now overhaul all the fields in `RlsProtoData.RouteLookupConfig` class based on the spec http://go/grpc-rls-lb-policy-design#heading=h.y3h669gfpown. Also move the validation logic in json parsing rather than in the constructor of `RouteLookupConfig`. --- .../java/io/grpc/rls/CachingRlsLbClient.java | 6 +- .../io/grpc/rls/RlsLoadBalancerProvider.java | 16 - .../java/io/grpc/rls/RlsProtoConverters.java | 120 ++++-- .../main/java/io/grpc/rls/RlsProtoData.java | 142 ++---- .../java/io/grpc/rls/RlsRequestFactory.java | 6 - .../io/grpc/rls/CachingRlsLbClientTest.java | 15 +- .../java/io/grpc/rls/RlsLoadBalancerTest.java | 8 +- .../io/grpc/rls/RlsProtoConvertersTest.java | 408 +++++++++++++++++- .../java/io/grpc/rls/RlsProtoDataTest.java | 56 --- .../io/grpc/rls/RlsRequestFactoryTest.java | 37 +- 10 files changed, 527 insertions(+), 287 deletions(-) delete mode 100644 rls/src/test/java/io/grpc/rls/RlsProtoDataTest.java diff --git a/rls/src/main/java/io/grpc/rls/CachingRlsLbClient.java b/rls/src/main/java/io/grpc/rls/CachingRlsLbClient.java index 87d01360f49..7bd9f4b68e3 100644 --- a/rls/src/main/java/io/grpc/rls/CachingRlsLbClient.java +++ b/rls/src/main/java/io/grpc/rls/CachingRlsLbClient.java @@ -128,9 +128,9 @@ private CachingRlsLbClient(Builder builder) { synchronizationContext = helper.getSynchronizationContext(); lbPolicyConfig = checkNotNull(builder.lbPolicyConfig, "lbPolicyConfig"); RouteLookupConfig rlsConfig = lbPolicyConfig.getRouteLookupConfig(); - maxAgeNanos = TimeUnit.MILLISECONDS.toNanos(rlsConfig.getMaxAgeInMillis()); - staleAgeNanos = TimeUnit.MILLISECONDS.toNanos(rlsConfig.getStaleAgeInMillis()); - callTimeoutNanos = TimeUnit.MILLISECONDS.toNanos(rlsConfig.getLookupServiceTimeoutInMillis()); + maxAgeNanos = rlsConfig.getMaxAgeInNanos(); + staleAgeNanos = rlsConfig.getStaleAgeInNanos(); + callTimeoutNanos = rlsConfig.getLookupServiceTimeoutInNanos(); timeProvider = checkNotNull(builder.timeProvider, "timeProvider"); throttler = checkNotNull(builder.throttler, "throttler"); linkedHashLruCache = diff --git a/rls/src/main/java/io/grpc/rls/RlsLoadBalancerProvider.java b/rls/src/main/java/io/grpc/rls/RlsLoadBalancerProvider.java index ca1ed714e82..3755fe3f77c 100644 --- a/rls/src/main/java/io/grpc/rls/RlsLoadBalancerProvider.java +++ b/rls/src/main/java/io/grpc/rls/RlsLoadBalancerProvider.java @@ -67,22 +67,6 @@ public ConfigOrError parseLoadBalancingPolicyConfig(Map rawLoadBalanc JsonUtil.getString(rawLoadBalancingConfigPolicy, "childPolicyConfigTargetFieldName"), JsonUtil.checkObjectList( checkNotNull(JsonUtil.getList(rawLoadBalancingConfigPolicy, "childPolicy")))); - // Checking all valid targets to make sure the config is always valid. This strict check - // prevents child policy to handle invalid child policy. - for (String validTarget : routeLookupConfig.getValidTargets()) { - ConfigOrError childPolicyConfigOrError = - lbPolicy - .getEffectiveLbProvider() - .parseLoadBalancingPolicyConfig(lbPolicy.getEffectiveChildPolicy(validTarget)); - if (childPolicyConfigOrError.getError() != null) { - return - ConfigOrError.fromError( - childPolicyConfigOrError - .getError() - .augmentDescription( - "failed to parse childPolicy for validTarget: " + validTarget)); - } - } return ConfigOrError.fromConfig(new LbPolicyConfiguration(routeLookupConfig, lbPolicy)); } catch (Exception e) { return ConfigOrError.fromError( diff --git a/rls/src/main/java/io/grpc/rls/RlsProtoConverters.java b/rls/src/main/java/io/grpc/rls/RlsProtoConverters.java index ce89def4467..b4d3fcc3b29 100644 --- a/rls/src/main/java/io/grpc/rls/RlsProtoConverters.java +++ b/rls/src/main/java/io/grpc/rls/RlsProtoConverters.java @@ -18,8 +18,12 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; +import static java.util.concurrent.TimeUnit.MINUTES; +import static java.util.concurrent.TimeUnit.SECONDS; import com.google.common.base.Converter; +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import io.grpc.internal.JsonUtil; import io.grpc.lookup.v1.RouteLookupRequest; @@ -29,10 +33,13 @@ import io.grpc.rls.RlsProtoData.GrpcKeyBuilder.Name; import io.grpc.rls.RlsProtoData.NameMatcher; import io.grpc.rls.RlsProtoData.RouteLookupConfig; +import java.net.URI; +import java.net.URISyntaxException; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.concurrent.TimeUnit; +import java.util.Set; import javax.annotation.Nullable; /** @@ -41,6 +48,12 @@ */ final class RlsProtoConverters { + private static final long MAX_AGE_NANOS = MINUTES.toNanos(5); + private static final long MAX_CACHE_SIZE = 5 * 1024 * 1024; // 5MiB + private static final long DEFAULT_LOOKUP_SERVICE_TIMEOUT = SECONDS.toNanos(10); + private static final ImmutableList EXTRA_KEY_NAMES = + ImmutableList.of("host", "service", "method"); + /** * RouteLookupRequestConverter converts between {@link RouteLookupRequest} and {@link * RlsProtoData.RouteLookupRequest}. @@ -96,31 +109,49 @@ static final class RouteLookupConfigConverter @Override protected RouteLookupConfig doForward(Map json) { List grpcKeyBuilders = - GrpcKeyBuilderConverter - .covertAll(JsonUtil.checkObjectList(JsonUtil.getList(json, "grpcKeyBuilders"))); + GrpcKeyBuilderConverter.covertAll( + checkNotNull(JsonUtil.getListOfObjects(json, "grpcKeyBuilders"), "grpcKeyBuilders")); + checkArgument(!grpcKeyBuilders.isEmpty(), "must have at least one GrpcKeyBuilder"); + Set names = new HashSet<>(); + for (GrpcKeyBuilder keyBuilder : grpcKeyBuilders) { + for (Name name : keyBuilder.getNames()) { + checkArgument(names.add(name), "duplicate names in grpc_keybuilders: " + name); + } + } String lookupService = JsonUtil.getString(json, "lookupService"); - long timeout = - TimeUnit.SECONDS.toMillis( - orDefault( - JsonUtil.getNumberAsLong(json, "lookupServiceTimeout"), - 0L)); - Long maxAge = - convertTimeIfNotNull( - TimeUnit.SECONDS, TimeUnit.MILLISECONDS, JsonUtil.getNumberAsLong(json, "maxAge")); - Long staleAge = - convertTimeIfNotNull( - TimeUnit.SECONDS, TimeUnit.MILLISECONDS, JsonUtil.getNumberAsLong(json, "staleAge")); - long cacheSize = orDefault(JsonUtil.getNumberAsLong(json, "cacheSizeBytes"), Long.MAX_VALUE); - List validTargets = JsonUtil.checkStringList(JsonUtil.getList(json, "validTargets")); - String defaultTarget = JsonUtil.getString(json, "defaultTarget"); + checkArgument(!Strings.isNullOrEmpty(lookupService), "lookupService must not be empty"); + try { + new URI(lookupService); + } catch (URISyntaxException e) { + throw new IllegalArgumentException( + "The lookupService field is not valid URI: " + lookupService, e); + } + long timeout = orDefault( + JsonUtil.getStringAsDuration(json, "lookupServiceTimeout"), + DEFAULT_LOOKUP_SERVICE_TIMEOUT); + checkArgument(timeout > 0, "lookupServiceTimeout should be positive"); + Long maxAge = JsonUtil.getStringAsDuration(json, "maxAge"); + Long staleAge = JsonUtil.getStringAsDuration(json, "staleAge"); + if (maxAge == null) { + checkArgument(staleAge == null, "to specify staleAge, must have maxAge"); + maxAge = MAX_AGE_NANOS; + } + if (staleAge == null) { + staleAge = MAX_AGE_NANOS; + } + maxAge = Math.min(maxAge, MAX_AGE_NANOS); + staleAge = Math.min(staleAge, maxAge); + long cacheSize = orDefault(JsonUtil.getNumberAsLong(json, "cacheSizeBytes"), MAX_CACHE_SIZE); + checkArgument(cacheSize > 0, "cacheSize must be positive"); + cacheSize = Math.min(cacheSize, MAX_CACHE_SIZE); + String defaultTarget = Strings.emptyToNull(JsonUtil.getString(json, "defaultTarget")); return new RouteLookupConfig( grpcKeyBuilders, lookupService, - /* lookupServiceTimeoutInMillis= */ timeout, - /* maxAgeInMillis= */ maxAge, - /* staleAgeInMillis= */ staleAge, + /* lookupServiceTimeoutInNanos= */ timeout, + /* maxAgeInNanos= */ maxAge, + /* staleAgeInNanos= */ staleAge, /* cacheSizeBytes= */ cacheSize, - validTargets, defaultTarget); } @@ -131,13 +162,6 @@ private static T orDefault(@Nullable T value, T defaultValue) { return value; } - private static Long convertTimeIfNotNull(TimeUnit from, TimeUnit to, Long value) { - if (value == null) { - return null; - } - return to.convert(value, from); - } - @Override protected Map doBackward(RouteLookupConfig routeLookupConfig) { throw new UnsupportedOperationException(); @@ -155,25 +179,29 @@ public static List covertAll(List> keyBuilders) { @SuppressWarnings("unchecked") static GrpcKeyBuilder convert(Map keyBuilder) { - List> rawNames = - JsonUtil.checkObjectList(JsonUtil.getList(keyBuilder, "names")); + List rawRawNames = JsonUtil.getList(keyBuilder, "names"); + checkArgument( + rawRawNames != null && !rawRawNames.isEmpty(), + "each keyBuilder must have at least one name"); + List> rawNames = JsonUtil.checkObjectList(rawRawNames); List names = new ArrayList<>(); for (Map rawName : rawNames) { - names.add( - new Name( - JsonUtil.getString(rawName, "service"), JsonUtil.getString(rawName, "method"))); + String serviceName = JsonUtil.getString(rawName, "service"); + checkArgument(!Strings.isNullOrEmpty(serviceName), "service must not be empty or null"); + names.add(new Name(serviceName, JsonUtil.getString(rawName, "method"))); } + List rawRawHeaders = JsonUtil.getList(keyBuilder, "headers"); List> rawHeaders = - JsonUtil.checkObjectList(JsonUtil.getList(keyBuilder, "headers")); + rawRawHeaders == null + ? new ArrayList>() : JsonUtil.checkObjectList(rawRawHeaders); List nameMatchers = new ArrayList<>(); for (Map rawHeader : rawHeaders) { - NameMatcher matcher = - new NameMatcher( - JsonUtil.getString(rawHeader, "key"), - (List) rawHeader.get("names"), - (Boolean) rawHeader.get("optional")); + Boolean requiredMatch = JsonUtil.getBoolean(rawHeader, "requiredMatch"); checkArgument( - matcher.isOptional(), "NameMatcher for GrpcKeyBuilders shouldn't be required"); + requiredMatch == null || !requiredMatch, + "requiredMatch shouldn't be specified for gRPC"); + NameMatcher matcher = new NameMatcher( + JsonUtil.getString(rawHeader, "key"), (List) rawHeader.get("names")); nameMatchers.add(matcher); } ExtraKeys extraKeys = ExtraKeys.DEFAULT; @@ -188,9 +216,21 @@ static GrpcKeyBuilder convert(Map keyBuilder) { if (constantKeys == null) { constantKeys = ImmutableMap.of(); } + checkUniqueKey(nameMatchers, constantKeys.keySet()); return new GrpcKeyBuilder(names, nameMatchers, extraKeys, constantKeys); } } + private static void checkUniqueKey(List nameMatchers, Set constantKeys) { + Set keys = new HashSet<>(constantKeys); + keys.addAll(EXTRA_KEY_NAMES); + for (NameMatcher nameMatcher : nameMatchers) { + keys.add(nameMatcher.getKey()); + } + if (keys.size() != nameMatchers.size() + constantKeys.size() + EXTRA_KEY_NAMES.size()) { + throw new IllegalArgumentException("keys in KeyBuilder must be unique"); + } + } + private RlsProtoConverters() {} } diff --git a/rls/src/main/java/io/grpc/rls/RlsProtoData.java b/rls/src/main/java/io/grpc/rls/RlsProtoData.java index 32dc81829b3..4709894833c 100644 --- a/rls/src/main/java/io/grpc/rls/RlsProtoData.java +++ b/rls/src/main/java/io/grpc/rls/RlsProtoData.java @@ -16,7 +16,6 @@ package io.grpc.rls; -import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; @@ -25,12 +24,8 @@ import com.google.common.base.Objects; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import io.grpc.rls.RlsProtoData.GrpcKeyBuilder.Name; -import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Set; -import java.util.concurrent.TimeUnit; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; @@ -142,66 +137,36 @@ public String toString() { @Immutable static final class RouteLookupConfig { - private static final long MAX_AGE_MILLIS = TimeUnit.MINUTES.toMillis(5); - private static final long MAX_CACHE_SIZE = 5 * 1024 * 1024; - private final ImmutableList grpcKeyBuilders; private final String lookupService; - private final long lookupServiceTimeoutInMillis; + private final long lookupServiceTimeoutInNanos; - private final long maxAgeInMillis; + private final long maxAgeInNanos; - private final long staleAgeInMillis; + private final long staleAgeInNanos; private final long cacheSizeBytes; - private final ImmutableList validTargets; - @Nullable private final String defaultTarget; RouteLookupConfig( List grpcKeyBuilders, String lookupService, - long lookupServiceTimeoutInMillis, - @Nullable Long maxAgeInMillis, - @Nullable Long staleAgeInMillis, + long lookupServiceTimeoutInNanos, + long maxAgeInNanos, + long staleAgeInNanos, long cacheSizeBytes, - List validTargets, @Nullable String defaultTarget) { - checkState( - !checkNotNull(grpcKeyBuilders, "grpcKeyBuilders").isEmpty(), - "must have at least one GrpcKeyBuilder"); - checkUniqueName(grpcKeyBuilders); this.grpcKeyBuilders = ImmutableList.copyOf(grpcKeyBuilders); - // TODO(creamsoup) also check if it is URI - checkState( - lookupService != null && !lookupService.isEmpty(), "lookupService must not be empty"); this.lookupService = lookupService; - checkState( - lookupServiceTimeoutInMillis > 0, "lookupServiceTimeoutInMillis should be positive"); - this.lookupServiceTimeoutInMillis = lookupServiceTimeoutInMillis; - if (maxAgeInMillis == null) { - checkState( - staleAgeInMillis == null, "To specify staleAgeInMillis, must have maxAgeInMillis"); - } - if (maxAgeInMillis == null || maxAgeInMillis == 0) { - maxAgeInMillis = MAX_AGE_MILLIS; - } - if (staleAgeInMillis == null || staleAgeInMillis == 0) { - staleAgeInMillis = MAX_AGE_MILLIS; - } - this.maxAgeInMillis = Math.min(maxAgeInMillis, MAX_AGE_MILLIS); - this.staleAgeInMillis = Math.min(staleAgeInMillis, this.maxAgeInMillis); - checkArgument(cacheSizeBytes > 0, "cacheSize must be positive"); - this.cacheSizeBytes = Math.min(cacheSizeBytes, MAX_CACHE_SIZE); - this.validTargets = ImmutableList.copyOf(checkNotNull(validTargets, "validTargets")); - if (defaultTarget != null && defaultTarget.isEmpty()) { - defaultTarget = null; - } + this.lookupServiceTimeoutInNanos = lookupServiceTimeoutInNanos; + this.maxAgeInNanos = maxAgeInNanos; + this.staleAgeInNanos = staleAgeInNanos; + this.cacheSizeBytes = cacheSizeBytes; this.defaultTarget = defaultTarget; } @@ -224,22 +189,22 @@ String getLookupService() { } /** Returns the timeout value for lookup service requests. */ - long getLookupServiceTimeoutInMillis() { - return lookupServiceTimeoutInMillis; + long getLookupServiceTimeoutInNanos() { + return lookupServiceTimeoutInNanos; } /** Returns the maximum age the result will be cached. */ - long getMaxAgeInMillis() { - return maxAgeInMillis; + long getMaxAgeInNanos() { + return maxAgeInNanos; } /** * Returns the time when an entry will be in a staled status. When cache is accessed whgen the * entry is in staled status, it will */ - long getStaleAgeInMillis() { - return staleAgeInMillis; + long getStaleAgeInNanos() { + return staleAgeInNanos; } /** @@ -252,15 +217,6 @@ long getCacheSizeBytes() { return cacheSizeBytes; } - /** - * Returns the list of all the possible targets that can be returned by the lookup service. If - * a target not on this list is returned, it will be treated the same as an RPC error from the - * RLS. - */ - ImmutableList getValidTargets() { - return validTargets; - } - /** * Returns the default target to use if needed. If nonempty (implies request processing * strategy SYNC_LOOKUP_DEFAULT_TARGET_ON_ERROR is set), it will be used if RLS returns an @@ -281,9 +237,9 @@ public boolean equals(Object o) { return false; } RouteLookupConfig that = (RouteLookupConfig) o; - return lookupServiceTimeoutInMillis == that.lookupServiceTimeoutInMillis - && maxAgeInMillis == that.maxAgeInMillis - && staleAgeInMillis == that.staleAgeInMillis + return lookupServiceTimeoutInNanos == that.lookupServiceTimeoutInNanos + && maxAgeInNanos == that.maxAgeInNanos + && staleAgeInNanos == that.staleAgeInNanos && cacheSizeBytes == that.cacheSizeBytes && Objects.equal(grpcKeyBuilders, that.grpcKeyBuilders) && Objects.equal(lookupService, that.lookupService) @@ -295,9 +251,9 @@ public int hashCode() { return Objects.hashCode( grpcKeyBuilders, lookupService, - lookupServiceTimeoutInMillis, - maxAgeInMillis, - staleAgeInMillis, + lookupServiceTimeoutInNanos, + maxAgeInNanos, + staleAgeInNanos, cacheSizeBytes, defaultTarget); } @@ -307,26 +263,15 @@ public String toString() { return MoreObjects.toStringHelper(this) .add("grpcKeyBuilders", grpcKeyBuilders) .add("lookupService", lookupService) - .add("lookupServiceTimeoutInMillis", lookupServiceTimeoutInMillis) - .add("maxAgeInMillis", maxAgeInMillis) - .add("staleAgeInMillis", staleAgeInMillis) + .add("lookupServiceTimeoutInNanos", lookupServiceTimeoutInNanos) + .add("maxAgeInNanos", maxAgeInNanos) + .add("staleAgeInNanos", staleAgeInNanos) .add("cacheSize", cacheSizeBytes) .add("defaultTarget", defaultTarget) .toString(); } } - private static void checkUniqueName(List grpcKeyBuilders) { - Set names = new HashSet<>(); - for (GrpcKeyBuilder grpcKeyBuilder : grpcKeyBuilders) { - int prevSize = names.size(); - names.addAll(grpcKeyBuilder.getNames()); - if (names.size() != prevSize + grpcKeyBuilder.getNames().size()) { - throw new IllegalStateException("Names in the GrpcKeyBuilders should be unique"); - } - } - } - /** * NameMatcher extract a key based on a given name (e.g. header name or query parameter name). * The name must match one of the names listed in the "name" field. If the "required_match" field @@ -339,12 +284,9 @@ static final class NameMatcher { private final ImmutableList names; - private final boolean optional; - - NameMatcher(String key, List names, @Nullable Boolean optional) { + NameMatcher(String key, List names) { this.key = checkNotNull(key, "key"); this.names = ImmutableList.copyOf(checkNotNull(names, "names")); - this.optional = optional != null ? optional : true; } /** The name that will be used in the RLS key_map to refer to this value. */ @@ -357,13 +299,6 @@ ImmutableList names() { return names; } - /** - * Indicates if this extraction optional. A key builder will still match if no value is found. - */ - boolean isOptional() { - return optional; - } - @Override public boolean equals(Object o) { if (this == o) { @@ -373,14 +308,13 @@ public boolean equals(Object o) { return false; } NameMatcher matcher = (NameMatcher) o; - return optional == matcher.optional - && java.util.Objects.equals(key, matcher.key) + return java.util.Objects.equals(key, matcher.key) && java.util.Objects.equals(names, matcher.names); } @Override public int hashCode() { - return java.util.Objects.hash(key, names, optional); + return java.util.Objects.hash(key, names); } @Override @@ -388,7 +322,6 @@ public String toString() { return MoreObjects.toStringHelper(this) .add("key", key) .add("names", names) - .add("optional", optional) .toString(); } } @@ -408,19 +341,11 @@ static final class GrpcKeyBuilder { Map constantKeys) { checkState(names != null && !names.isEmpty(), "names cannot be empty"); this.names = ImmutableList.copyOf(names); - checkUniqueKey(checkNotNull(headers, "headers")); this.headers = ImmutableList.copyOf(headers); this.extraKeys = checkNotNull(extraKeys, "extraKeys"); this.constantKeys = ImmutableMap.copyOf(checkNotNull(constantKeys, "constantKeys")); } - private static void checkUniqueKey(List headers) { - Set names = new HashSet<>(); - for (NameMatcher header : headers) { - checkState(names.add(header.key), "key in headers must be unique"); - } - } - /** * Returns names. To match, one of the given Name fields must match; the service and method * fields are specified as fixed strings. The service name is required and includes the proto @@ -486,17 +411,11 @@ static final class Name { private final String service; + @Nullable private final String method; - public Name(String service) { - this(service, "*"); - } - /** The primary constructor. */ - Name(String service, String method) { - checkState( - !checkNotNull(service, "service").isEmpty(), - "service must not be empty or null"); + Name(String service, @Nullable String method) { this.service = service; this.method = method; } @@ -505,6 +424,7 @@ String getService() { return service; } + @Nullable String getMethod() { return method; } diff --git a/rls/src/main/java/io/grpc/rls/RlsRequestFactory.java b/rls/src/main/java/io/grpc/rls/RlsRequestFactory.java index e181d64833d..d3aecabd034 100644 --- a/rls/src/main/java/io/grpc/rls/RlsRequestFactory.java +++ b/rls/src/main/java/io/grpc/rls/RlsRequestFactory.java @@ -21,8 +21,6 @@ import com.google.common.base.MoreObjects; import com.google.common.collect.ImmutableMap; import io.grpc.Metadata; -import io.grpc.Status; -import io.grpc.StatusRuntimeException; import io.grpc.rls.RlsProtoData.ExtraKeys; import io.grpc.rls.RlsProtoData.GrpcKeyBuilder; import io.grpc.rls.RlsProtoData.GrpcKeyBuilder.Name; @@ -107,10 +105,6 @@ private Map createRequestHeaders( } if (value != null) { rlsRequestHeaders.put(nameMatcher.getKey(), value); - } else if (!nameMatcher.isOptional()) { - throw new StatusRuntimeException( - Status.INVALID_ARGUMENT.withDescription( - String.format("Missing mandatory metadata(%s) not found", nameMatcher.getKey()))); } } return rlsRequestHeaders; diff --git a/rls/src/test/java/io/grpc/rls/CachingRlsLbClientTest.java b/rls/src/test/java/io/grpc/rls/CachingRlsLbClientTest.java index aa64ec890b6..c8222a02b8a 100644 --- a/rls/src/test/java/io/grpc/rls/CachingRlsLbClientTest.java +++ b/rls/src/test/java/io/grpc/rls/CachingRlsLbClientTest.java @@ -210,7 +210,7 @@ public void get_noError_lifeCycle() throws Exception { assertThat(resp.hasData()).isTrue(); // cache hit for staled entry - fakeTimeProvider.forwardTime(ROUTE_LOOKUP_CONFIG.getStaleAgeInMillis(), TimeUnit.MILLISECONDS); + fakeTimeProvider.forwardTime(ROUTE_LOOKUP_CONFIG.getStaleAgeInNanos(), TimeUnit.NANOSECONDS); resp = getInSyncContext(routeLookupRequest); assertThat(resp.hasData()).isTrue(); @@ -226,7 +226,7 @@ public void get_noError_lifeCycle() throws Exception { assertThat(resp.hasData()).isTrue(); // existing cache expired - fakeTimeProvider.forwardTime(ROUTE_LOOKUP_CONFIG.getMaxAgeInMillis(), TimeUnit.MILLISECONDS); + fakeTimeProvider.forwardTime(ROUTE_LOOKUP_CONFIG.getMaxAgeInNanos(), TimeUnit.NANOSECONDS); resp = getInSyncContext(routeLookupRequest); @@ -423,16 +423,15 @@ private static RouteLookupConfig getRouteLookupConfig() { new GrpcKeyBuilder( ImmutableList.of(new Name("service1", "create")), ImmutableList.of( - new NameMatcher("user", ImmutableList.of("User", "Parent"), true), - new NameMatcher("id", ImmutableList.of("X-Google-Id"), true)), + new NameMatcher("user", ImmutableList.of("User", "Parent")), + new NameMatcher("id", ImmutableList.of("X-Google-Id"))), ExtraKeys.create("server", "service-key", "method-key"), ImmutableMap.of())), /* lookupService= */ "service1", - /* lookupServiceTimeoutInMillis= */ TimeUnit.SECONDS.toMillis(2), - /* maxAgeInMillis= */ TimeUnit.SECONDS.toMillis(300), - /* staleAgeInMillis= */ TimeUnit.SECONDS.toMillis(240), + /* lookupServiceTimeoutInNanos= */ TimeUnit.SECONDS.toNanos(2), + /* maxAgeInNanos= */ TimeUnit.SECONDS.toNanos(300), + /* staleAgeInNanos= */ TimeUnit.SECONDS.toNanos(240), /* cacheSizeBytes= */ 1000, - /* validTargets= */ ImmutableList.of("a valid target"), DEFAULT_TARGET); } diff --git a/rls/src/test/java/io/grpc/rls/RlsLoadBalancerTest.java b/rls/src/test/java/io/grpc/rls/RlsLoadBalancerTest.java index fba295f98f0..bf0bc47fcc4 100644 --- a/rls/src/test/java/io/grpc/rls/RlsLoadBalancerTest.java +++ b/rls/src/test/java/io/grpc/rls/RlsLoadBalancerTest.java @@ -420,11 +420,11 @@ private String getRlsConfigJsonStr() { + " }\n" + " ],\n" + " \"lookupService\": \"localhost:8972\",\n" - + " \"lookupServiceTimeout\": 2,\n" - + " \"maxAge\": 300,\n" - + " \"staleAge\": 240,\n" + + " \"lookupServiceTimeout\": \"2s\",\n" + + " \"maxAge\": \"300s\",\n" + + " \"staleAge\": \"240s\",\n" + " \"validTargets\": [\"localhost:9001\", \"localhost:9002\"]," - + " \"cacheSizeBytes\": 1000,\n" + + " \"cacheSizeBytes\": \"1000\",\n" + " \"defaultTarget\": \"" + defaultTarget + "\",\n" + " \"requestProcessingStrategy\": \"SYNC_LOOKUP_DEFAULT_TARGET_ON_ERROR\"\n" + "}"; diff --git a/rls/src/test/java/io/grpc/rls/RlsProtoConvertersTest.java b/rls/src/test/java/io/grpc/rls/RlsProtoConvertersTest.java index bfb331e6cc8..5d1dcc1cadb 100644 --- a/rls/src/test/java/io/grpc/rls/RlsProtoConvertersTest.java +++ b/rls/src/test/java/io/grpc/rls/RlsProtoConvertersTest.java @@ -17,6 +17,7 @@ package io.grpc.rls; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.fail; import com.google.common.base.Converter; import com.google.common.collect.ImmutableList; @@ -166,11 +167,11 @@ public void convert_jsonRlsConfig() throws IOException { + " }\n" + " ],\n" + " \"lookupService\": \"service1\",\n" - + " \"lookupServiceTimeout\": 2,\n" - + " \"maxAge\": 300,\n" - + " \"staleAge\": 240,\n" + + " \"lookupServiceTimeout\": \"2s\",\n" + + " \"maxAge\": \"300s\",\n" + + " \"staleAge\": \"240s\",\n" + " \"validTargets\": [\"a valid target\"]," - + " \"cacheSizeBytes\": 1000,\n" + + " \"cacheSizeBytes\": \"1000\",\n" + " \"defaultTarget\": \"us_east_1.cloudbigtable.googleapis.com\"\n" + "}"; @@ -180,29 +181,28 @@ public void convert_jsonRlsConfig() throws IOException { new GrpcKeyBuilder( ImmutableList.of(new Name("service1", "create")), ImmutableList.of( - new NameMatcher("user", ImmutableList.of("User", "Parent"), true), - new NameMatcher("id", ImmutableList.of("X-Google-Id"), true)), + new NameMatcher("user", ImmutableList.of("User", "Parent")), + new NameMatcher("id", ImmutableList.of("X-Google-Id"))), ExtraKeys.DEFAULT, ImmutableMap.of()), new GrpcKeyBuilder( - ImmutableList.of(new Name("service1")), + ImmutableList.of(new Name("service1", "*")), ImmutableList.of( - new NameMatcher("user", ImmutableList.of("User", "Parent"), true), - new NameMatcher("password", ImmutableList.of("Password"), true)), + new NameMatcher("user", ImmutableList.of("User", "Parent")), + new NameMatcher("password", ImmutableList.of("Password"))), ExtraKeys.DEFAULT, ImmutableMap.of()), new GrpcKeyBuilder( - ImmutableList.of(new Name("service3")), + ImmutableList.of(new Name("service3", "*")), ImmutableList.of( - new NameMatcher("user", ImmutableList.of("User", "Parent"), true)), + new NameMatcher("user", ImmutableList.of("User", "Parent"))), ExtraKeys.create("host-key", "service-key", "method-key"), ImmutableMap.of("constKey1", "value1"))), /* lookupService= */ "service1", - /* lookupServiceTimeoutInMillis= */ TimeUnit.SECONDS.toMillis(2), - /* maxAgeInMillis= */ TimeUnit.SECONDS.toMillis(300), - /* staleAgeInMillis= */ TimeUnit.SECONDS.toMillis(240), + /* lookupServiceTimeoutInMillis= */ TimeUnit.SECONDS.toNanos(2), + /* maxAgeInNanos= */ TimeUnit.SECONDS.toNanos(300), + /* staleAgeInNanos= */ TimeUnit.SECONDS.toNanos(240), /* cacheSizeBytes= */ 1000, - /* validTargets= */ ImmutableList.of("a valid target"), /* defaultTarget= */ "us_east_1.cloudbigtable.googleapis.com"); RouteLookupConfigConverter converter = new RouteLookupConfigConverter(); @@ -211,4 +211,382 @@ public void convert_jsonRlsConfig() throws IOException { RouteLookupConfig converted = converter.convert(parsedJson); assertThat(converted).isEqualTo(expectedConfig); } + + @Test + public void convert_jsonRlsConfig_emptyKeyBuilders() throws IOException { + String jsonStr = "{\n" + + " \"grpcKeyBuilders\": [],\n" + + " \"lookupService\": \"service1\",\n" + + " \"lookupServiceTimeout\": \"2s\",\n" + + " \"maxAge\": \"300s\",\n" + + " \"staleAge\": \"240s\",\n" + + " \"validTargets\": [\"a valid target\"]," + + " \"cacheSizeBytes\": \"1000\",\n" + + " \"defaultTarget\": \"us_east_1.cloudbigtable.googleapis.com\"\n" + + "}"; + + RouteLookupConfigConverter converter = new RouteLookupConfigConverter(); + @SuppressWarnings("unchecked") + Map parsedJson = (Map) JsonParser.parse(jsonStr); + try { + converter.convert(parsedJson); + fail("Exception expected"); + } catch (IllegalArgumentException e) { + assertThat(e).hasMessageThat().contains("must have at least one GrpcKeyBuilder"); + } + } + + @Test + public void convert_jsonRlsConfig_namesNotUnique() throws IOException { + String jsonStr = "{\n" + + " \"grpcKeyBuilders\": [\n" + + " {\n" + + " \"names\": [\n" + + " {\n" + + " \"service\": \"service1\",\n" + + " \"method\": \"create\"\n" + + " }\n" + + " ],\n" + + " \"headers\": [\n" + + " {\n" + + " \"key\": \"user\"," + + " \"names\": [\"User\", \"Parent\"],\n" + + " \"optional\": true\n" + + " },\n" + + " {\n" + + " \"key\": \"id\"," + + " \"names\": [\"X-Google-Id\"],\n" + + " \"optional\": true\n" + + " }\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"names\": [\n" + + " {\n" + + " \"service\": \"service1\",\n" + + " \"method\": \"create\"\n" + + " }\n" + + " ],\n" + + " \"headers\": [\n" + + " {\n" + + " \"key\": \"user\"," + + " \"names\": [\"User\", \"Parent\"],\n" + + " \"optional\": true\n" + + " },\n" + + " {\n" + + " \"key\": \"password\"," + + " \"names\": [\"Password\"],\n" + + " \"optional\": true\n" + + " }\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"names\": [\n" + + " {\n" + + " \"service\": \"service3\",\n" + + " \"method\": \"*\"\n" + + " }\n" + + " ],\n" + + " \"headers\": [" + + " {\n" + + " \"key\": \"user\"," + + " \"names\": [\"User\", \"Parent\"],\n" + + " \"optional\": true\n" + + " }\n" + + " ],\n" + + " \"extraKeys\": {\n" + + " \"host\": \"host-key\",\n" + + " \"service\": \"service-key\",\n" + + " \"method\": \"method-key\"\n" + + " }, \n" + + " \"constantKeys\": {\n" + + " \"constKey1\": \"value1\"\n" + + " }\n" + + " }\n" + + " ],\n" + + " \"lookupService\": \"service1\",\n" + + " \"lookupServiceTimeout\": \"2s\",\n" + + " \"maxAge\": \"300s\",\n" + + " \"staleAge\": \"240s\",\n" + + " \"validTargets\": [\"a valid target\"]," + + " \"cacheSizeBytes\": \"1000\",\n" + + " \"defaultTarget\": \"us_east_1.cloudbigtable.googleapis.com\"\n" + + "}"; + + RouteLookupConfigConverter converter = new RouteLookupConfigConverter(); + @SuppressWarnings("unchecked") + Map parsedJson = (Map) JsonParser.parse(jsonStr); + try { + converter.convert(parsedJson); + fail("Exception expected"); + } catch (IllegalArgumentException e) { + assertThat(e).hasMessageThat() + .contains("duplicate names in grpc_keybuilders: Name{service=service1, method=create}"); + } + } + + @Test + public void convert_jsonRlsConfig_defaultValues() throws IOException { + String jsonStr = "{\n" + + " \"grpcKeyBuilders\": [\n" + + " {\n" + + " \"names\": [\n" + + " {\n" + + " \"service\": \"service1\"\n" + + " }\n" + + " ]\n" + + " }\n" + + " ],\n" + + " \"lookupService\": \"service1\",\n" + + " \"validTargets\": [\"a valid target\"]," + + " \"defaultTarget\": \"us_east_1.cloudbigtable.googleapis.com\"\n" + + "}"; + + RouteLookupConfig expectedConfig = + new RouteLookupConfig( + ImmutableList.of( + new GrpcKeyBuilder( + ImmutableList.of(new Name("service1", null)), + ImmutableList.of(), + ExtraKeys.DEFAULT, + ImmutableMap.of())), + /* lookupService= */ "service1", + /* lookupServiceTimeoutInMillis= */ TimeUnit.SECONDS.toNanos(10), + /* maxAgeInNanos= */ TimeUnit.MINUTES.toNanos(5), + /* staleAgeInNanos= */ TimeUnit.MINUTES.toNanos(5), + /* cacheSizeBytes= */ 5 * 1024 * 1024, + /* defaultTarget= */ "us_east_1.cloudbigtable.googleapis.com"); + + RouteLookupConfigConverter converter = new RouteLookupConfigConverter(); + @SuppressWarnings("unchecked") + Map parsedJson = (Map) JsonParser.parse(jsonStr); + RouteLookupConfig converted = converter.convert(parsedJson); + assertThat(converted).isEqualTo(expectedConfig); + } + + @Test + public void convert_jsonRlsConfig_staleAgeCappedByMaxAge() throws IOException { + String jsonStr = "{\n" + + " \"grpcKeyBuilders\": [\n" + + " {\n" + + " \"names\": [\n" + + " {\n" + + " \"service\": \"service1\",\n" + + " \"method\": \"create\"\n" + + " }\n" + + " ],\n" + + " \"headers\": [\n" + + " {\n" + + " \"key\": \"user\"," + + " \"names\": [\"User\", \"Parent\"],\n" + + " \"optional\": true\n" + + " },\n" + + " {\n" + + " \"key\": \"id\"," + + " \"names\": [\"X-Google-Id\"],\n" + + " \"optional\": true\n" + + " }\n" + + " ]\n" + + " }\n" + + " ],\n" + + " \"lookupService\": \"service1\",\n" + + " \"lookupServiceTimeout\": \"2s\",\n" + + " \"maxAge\": \"300s\",\n" + + " \"staleAge\": \"400s\",\n" + + " \"validTargets\": [\"a valid target\"]," + + " \"cacheSizeBytes\": \"1000\",\n" + + " \"defaultTarget\": \"us_east_1.cloudbigtable.googleapis.com\"\n" + + "}"; + + RouteLookupConfig expectedConfig = + new RouteLookupConfig( + ImmutableList.of( + new GrpcKeyBuilder( + ImmutableList.of(new Name("service1", "create")), + ImmutableList.of( + new NameMatcher("user", ImmutableList.of("User", "Parent")), + new NameMatcher("id", ImmutableList.of("X-Google-Id"))), + ExtraKeys.DEFAULT, + ImmutableMap.of())), + /* lookupService= */ "service1", + /* lookupServiceTimeoutInMillis= */ TimeUnit.SECONDS.toNanos(2), + /* maxAgeInNanos= */ TimeUnit.SECONDS.toNanos(300), + /* staleAgeInNanos= */ TimeUnit.SECONDS.toNanos(300), + /* cacheSizeBytes= */ 1000, + /* defaultTarget= */ "us_east_1.cloudbigtable.googleapis.com"); + + RouteLookupConfigConverter converter = new RouteLookupConfigConverter(); + @SuppressWarnings("unchecked") + Map parsedJson = (Map) JsonParser.parse(jsonStr); + RouteLookupConfig converted = converter.convert(parsedJson); + assertThat(converted).isEqualTo(expectedConfig); + } + + @Test + public void convert_jsonRlsConfig_staleAgeGivenWithoutMaxAge() throws IOException { + String jsonStr = "{\n" + + " \"grpcKeyBuilders\": [\n" + + " {\n" + + " \"names\": [\n" + + " {\n" + + " \"service\": \"service1\",\n" + + " \"method\": \"create\"\n" + + " }\n" + + " ],\n" + + " \"headers\": [\n" + + " {\n" + + " \"key\": \"user\"," + + " \"names\": [\"User\", \"Parent\"],\n" + + " \"optional\": true\n" + + " },\n" + + " {\n" + + " \"key\": \"id\"," + + " \"names\": [\"X-Google-Id\"],\n" + + " \"optional\": true\n" + + " }\n" + + " ]\n" + + " }\n" + + " ],\n" + + " \"lookupService\": \"service1\",\n" + + " \"lookupServiceTimeout\": \"2s\",\n" + + " \"staleAge\": \"240s\",\n" + + " \"validTargets\": [\"a valid target\"]," + + " \"cacheSizeBytes\": \"1000\",\n" + + " \"defaultTarget\": \"us_east_1.cloudbigtable.googleapis.com\"\n" + + "}"; + + RouteLookupConfigConverter converter = new RouteLookupConfigConverter(); + @SuppressWarnings("unchecked") + Map parsedJson = (Map) JsonParser.parse(jsonStr); + try { + converter.convert(parsedJson); + fail("Exception expected"); + } catch (IllegalArgumentException e) { + assertThat(e).hasMessageThat().contains("to specify staleAge, must have maxAge"); + } + } + + @Test + public void convert_jsonRlsConfig_keyBuilderWithoutName() throws IOException { + String jsonStr = "{\n" + + " \"grpcKeyBuilders\": [\n" + + " {\n" + + " \"headers\": [\n" + + " {\n" + + " \"key\": \"user\"," + + " \"names\": [\"User\", \"Parent\"],\n" + + " \"optional\": true\n" + + " },\n" + + " {\n" + + " \"key\": \"id\"," + + " \"names\": [\"X-Google-Id\"],\n" + + " \"optional\": true\n" + + " }\n" + + " ]\n" + + " }\n" + + " ],\n" + + " \"lookupService\": \"service1\",\n" + + " \"lookupServiceTimeout\": \"2s\",\n" + + " \"staleAge\": \"240s\",\n" + + " \"validTargets\": [\"a valid target\"]," + + " \"cacheSizeBytes\": \"1000\",\n" + + " \"defaultTarget\": \"us_east_1.cloudbigtable.googleapis.com\"\n" + + "}"; + + RouteLookupConfigConverter converter = new RouteLookupConfigConverter(); + @SuppressWarnings("unchecked") + Map parsedJson = (Map) JsonParser.parse(jsonStr); + try { + converter.convert(parsedJson); + fail("Exception expected"); + } catch (IllegalArgumentException e) { + assertThat(e).hasMessageThat().contains("each keyBuilder must have at least one name"); + } + } + + @Test + public void convert_jsonRlsConfig_nameWithoutService() throws IOException { + String jsonStr = "{\n" + + " \"grpcKeyBuilders\": [\n" + + " {\n" + + " \"names\": [\n" + + " {\n" + + " \"method\": \"create\"\n" + + " }\n" + + " ],\n" + + " \"headers\": [\n" + + " {\n" + + " \"key\": \"user\"," + + " \"names\": [\"User\", \"Parent\"],\n" + + " \"optional\": true\n" + + " },\n" + + " {\n" + + " \"key\": \"id\"," + + " \"names\": [\"X-Google-Id\"],\n" + + " \"optional\": true\n" + + " }\n" + + " ]\n" + + " }\n" + + " ],\n" + + " \"lookupService\": \"service1\",\n" + + " \"lookupServiceTimeout\": \"2s\",\n" + + " \"staleAge\": \"240s\",\n" + + " \"validTargets\": [\"a valid target\"]," + + " \"cacheSizeBytes\": \"1000\",\n" + + " \"defaultTarget\": \"us_east_1.cloudbigtable.googleapis.com\"\n" + + "}"; + + RouteLookupConfigConverter converter = new RouteLookupConfigConverter(); + @SuppressWarnings("unchecked") + Map parsedJson = (Map) JsonParser.parse(jsonStr); + try { + converter.convert(parsedJson); + fail("Exception expected"); + } catch (IllegalArgumentException e) { + assertThat(e).hasMessageThat().contains("service must not be empty or null"); + } + } + + @Test + public void convert_jsonRlsConfig_keysNotUnique() throws IOException { + String jsonStr = "{\n" + + " \"grpcKeyBuilders\": [\n" + + " {\n" + + " \"names\": [\n" + + " {\n" + + " \"service\": \"service1\"\n" + + " }\n" + + " ],\n" + + " \"headers\": [\n" + + " {\n" + + " \"key\": \"service\"," // duplicate to extra_keys + + " \"names\": [\"User\", \"Parent\"],\n" + + " \"optional\": true\n" + + " },\n" + + " {\n" + + " \"key\": \"id\"," + + " \"names\": [\"X-Google-Id\"],\n" + + " \"optional\": true\n" + + " }\n" + + " ]\n" + + " }\n" + + " ],\n" + + " \"lookupService\": \"service1\",\n" + + " \"lookupServiceTimeout\": \"2s\",\n" + + " \"staleAge\": \"240s\",\n" + + " \"validTargets\": [\"a valid target\"]," + + " \"cacheSizeBytes\": \"1000\",\n" + + " \"defaultTarget\": \"us_east_1.cloudbigtable.googleapis.com\"\n" + + "}"; + + RouteLookupConfigConverter converter = new RouteLookupConfigConverter(); + @SuppressWarnings("unchecked") + Map parsedJson = (Map) JsonParser.parse(jsonStr); + try { + converter.convert(parsedJson); + fail("Exception expected"); + } catch (IllegalArgumentException e) { + assertThat(e).hasMessageThat().contains("keys in KeyBuilder must be unique"); + } + } } diff --git a/rls/src/test/java/io/grpc/rls/RlsProtoDataTest.java b/rls/src/test/java/io/grpc/rls/RlsProtoDataTest.java deleted file mode 100644 index 9cfb6c753fb..00000000000 --- a/rls/src/test/java/io/grpc/rls/RlsProtoDataTest.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2021 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; - -import static com.google.common.truth.Truth.assertThat; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import io.grpc.rls.RlsProtoData.ExtraKeys; -import io.grpc.rls.RlsProtoData.GrpcKeyBuilder; -import io.grpc.rls.RlsProtoData.GrpcKeyBuilder.Name; -import io.grpc.rls.RlsProtoData.NameMatcher; -import io.grpc.rls.RlsProtoData.RouteLookupConfig; -import java.util.concurrent.TimeUnit; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -/** Tests for {@link RlsProtoData}. */ -@RunWith(JUnit4.class) -public class RlsProtoDataTest { - @Test - public void maxCacheSize() { - RouteLookupConfig config = new RouteLookupConfig( - ImmutableList.of( - new GrpcKeyBuilder( - ImmutableList.of(new Name("service1", "create")), - ImmutableList.of( - new NameMatcher("user", ImmutableList.of("User", "Parent"), true), - new NameMatcher("id", ImmutableList.of("X-Google-Id"), true)), - ExtraKeys.create("server", "service-key", "method-key"), - ImmutableMap.of())), - "service1", - /* lookupServiceTimeoutInMillis= */ TimeUnit.SECONDS.toMillis(2), - /* maxAgeInMillis= */ TimeUnit.SECONDS.toMillis(300), - /* staleAgeInMillis= */ TimeUnit.SECONDS.toMillis(240), - /* cacheSizeBytes= */ 20 * 1000 * 1000, - ImmutableList.of("a-valid-target"), - "default-target"); - assertThat(config.getCacheSizeBytes()).isEqualTo(5 * 1024 * 1024); - } -} diff --git a/rls/src/test/java/io/grpc/rls/RlsRequestFactoryTest.java b/rls/src/test/java/io/grpc/rls/RlsRequestFactoryTest.java index b0d197ff525..0d834601279 100644 --- a/rls/src/test/java/io/grpc/rls/RlsRequestFactoryTest.java +++ b/rls/src/test/java/io/grpc/rls/RlsRequestFactoryTest.java @@ -17,13 +17,10 @@ package io.grpc.rls; import static com.google.common.truth.Truth.assertThat; -import static org.junit.Assert.fail; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import io.grpc.Metadata; -import io.grpc.Status.Code; -import io.grpc.StatusRuntimeException; import io.grpc.rls.RlsProtoData.ExtraKeys; import io.grpc.rls.RlsProtoData.GrpcKeyBuilder; import io.grpc.rls.RlsProtoData.GrpcKeyBuilder.Name; @@ -44,28 +41,27 @@ public class RlsRequestFactoryTest { 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 NameMatcher("user", ImmutableList.of("User", "Parent")), + new NameMatcher("id", ImmutableList.of("X-Google-Id"))), ExtraKeys.create("server-1", null, null), ImmutableMap.of("const-key-1", "const-value-1")), new GrpcKeyBuilder( - ImmutableList.of(new Name("com.google.service1")), + 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 NameMatcher("user", ImmutableList.of("User", "Parent")), + new NameMatcher("password", ImmutableList.of("Password"))), ExtraKeys.create(null, "service-2", null), ImmutableMap.of("const-key-2", "const-value-2")), new GrpcKeyBuilder( - ImmutableList.of(new Name("com.google.service2")), + 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 NameMatcher("password", ImmutableList.of("Password"))), ExtraKeys.create(null, "service-3", "method-3"), ImmutableMap.of()), new GrpcKeyBuilder( - ImmutableList.of(new Name("com.google.service3")), + ImmutableList.of(new Name("com.google.service3", "*")), ImmutableList.of( - new NameMatcher("user", ImmutableList.of("User", "Parent"), true)), + new NameMatcher("user", ImmutableList.of("User", "Parent"))), ExtraKeys.create(null, null, null), ImmutableMap.of("const-key-4", "const-value-4"))), /* lookupService= */ "bigtable-rls.googleapis.com", @@ -73,7 +69,6 @@ public class RlsRequestFactoryTest { /* maxAgeInMillis= */ TimeUnit.SECONDS.toMillis(300), /* staleAgeInMillis= */ TimeUnit.SECONDS.toMillis(240), /* cacheSizeBytes= */ 1000, - /* validTargets= */ ImmutableList.of("a valid target"), /* defaultTarget= */ "us_east_1.cloudbigtable.googleapis.com"); private final RlsRequestFactory factory = new RlsRequestFactory( @@ -94,20 +89,6 @@ public void create_pathMatches() { "const-key-1", "const-value-1"); } - @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(); From 766ed87e97b3f225cf5ddd1061d8c6df4e091d9a Mon Sep 17 00:00:00 2001 From: apolcyn Date: Tue, 7 Dec 2021 15:43:09 -0800 Subject: [PATCH 47/50] Replace C2P resolver env var with experimental scheme suffix (#8744) (#8747) Java analogue of grpc/grpc#28294 --- .../grpc/xds/GoogleCloudToProdNameResolverProvider.java | 9 ++------- .../xds/GoogleCloudToProdNameResolverProviderTest.java | 3 ++- .../io/grpc/xds/GoogleCloudToProdNameResolverTest.java | 2 +- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/xds/src/main/java/io/grpc/xds/GoogleCloudToProdNameResolverProvider.java b/xds/src/main/java/io/grpc/xds/GoogleCloudToProdNameResolverProvider.java index be8324b93cf..e7f9cb45ff8 100644 --- a/xds/src/main/java/io/grpc/xds/GoogleCloudToProdNameResolverProvider.java +++ b/xds/src/main/java/io/grpc/xds/GoogleCloudToProdNameResolverProvider.java @@ -29,9 +29,7 @@ @Internal public final class GoogleCloudToProdNameResolverProvider extends NameResolverProvider { - private static final boolean enableC2PResolver = - Boolean.parseBoolean(System.getenv("GRPC_EXPERIMENTAL_GOOGLE_C2P_RESOLVER")); - private static final String SCHEME = "google-c2p"; + private static final String SCHEME = "google-c2p-experimental"; @Override public NameResolver newNameResolver(URI targetUri, Args args) { @@ -50,10 +48,7 @@ public String getDefaultScheme() { @Override protected boolean isAvailable() { - if (enableC2PResolver) { - return true; - } - return false; + return true; } @Override diff --git a/xds/src/test/java/io/grpc/xds/GoogleCloudToProdNameResolverProviderTest.java b/xds/src/test/java/io/grpc/xds/GoogleCloudToProdNameResolverProviderTest.java index 372c8af8f41..cc6621028c8 100644 --- a/xds/src/test/java/io/grpc/xds/GoogleCloudToProdNameResolverProviderTest.java +++ b/xds/src/test/java/io/grpc/xds/GoogleCloudToProdNameResolverProviderTest.java @@ -73,7 +73,8 @@ NameResolverProvider.class, getClass().getClassLoader())) { @Test public void newNameResolver() { - assertThat(provider.newNameResolver(URI.create("google-c2p:///foo.googleapis.com"), args)) + assertThat(provider + .newNameResolver(URI.create("google-c2p-experimental:///foo.googleapis.com"), args)) .isInstanceOf(GoogleCloudToProdNameResolver.class); } } diff --git a/xds/src/test/java/io/grpc/xds/GoogleCloudToProdNameResolverTest.java b/xds/src/test/java/io/grpc/xds/GoogleCloudToProdNameResolverTest.java index 421b2a1dd0a..58be7108405 100644 --- a/xds/src/test/java/io/grpc/xds/GoogleCloudToProdNameResolverTest.java +++ b/xds/src/test/java/io/grpc/xds/GoogleCloudToProdNameResolverTest.java @@ -69,7 +69,7 @@ public class GoogleCloudToProdNameResolverTest { @Rule public final MockitoRule mocks = MockitoJUnit.rule(); - private static final URI TARGET_URI = URI.create("google-c2p:///googleapis.com"); + private static final URI TARGET_URI = URI.create("google-c2p-experimental:///googleapis.com"); private static final String ZONE = "us-central1-a"; private static final int DEFAULT_PORT = 887; From 43d01dbf3069e611f5cdd402d1303fbc2b6c890f Mon Sep 17 00:00:00 2001 From: Eric Anderson Date: Tue, 7 Dec 2021 16:18:46 -0800 Subject: [PATCH 48/50] Upgrade Protobuf to 3.19.1 and Guava to 30.1.1 Protobuf uses Guava 30.1.1, so I upgrade it at the same time. It also caused an update to rules_jvm_external and reworking the Bazel build. Protobuf no longer requires bind() so they were dropped. Although Protobuf's protobuf_deps() brings in rules_jvm_external, and so we don't need to define it ourselves, it seems better to define it directly and not depend on transitive deps since we use it directly. Protobuf now has support for maven_install() by exposing PROTOBUF_MAVEN_ARTIFACTS, which required reorganizing the WORKSPACE to use maven_install() after loading protobuf. Protobuf still doesn't define target overrides for itself so we still maintain those. When reorganizing the WORKSPACE I noticed http_archive should ideally be above io_grpc_grpc_java as most users will need it there, so I fixed that since there were lots of other load()-reordering already. --- COMPILING.md | 4 +-- WORKSPACE | 24 +++++++-------- build.gradle | 4 +-- buildscripts/make_dependencies.bat | 2 +- buildscripts/make_dependencies.sh | 2 +- examples/WORKSPACE | 30 +++++++++---------- examples/android/clientcache/app/build.gradle | 2 +- examples/android/helloworld/app/build.gradle | 2 +- examples/android/routeguide/app/build.gradle | 2 +- examples/android/strictmode/app/build.gradle | 2 +- examples/build.gradle | 2 +- examples/example-alts/build.gradle | 2 +- examples/example-gauth/build.gradle | 2 +- examples/example-gauth/pom.xml | 2 +- examples/example-hostname/build.gradle | 2 +- examples/example-hostname/pom.xml | 2 +- examples/example-jwt-auth/build.gradle | 2 +- examples/example-jwt-auth/pom.xml | 4 +-- examples/example-tls/build.gradle | 2 +- examples/example-tls/pom.xml | 2 +- examples/example-xds/build.gradle | 2 +- examples/pom.xml | 4 +-- repositories.bzl | 27 +++++------------ 23 files changed, 58 insertions(+), 71 deletions(-) diff --git a/COMPILING.md b/COMPILING.md index 208f05e25cb..d065e702842 100644 --- a/COMPILING.md +++ b/COMPILING.md @@ -43,11 +43,11 @@ This section is only necessary if you are making changes to the code generation. Most users only need to use `skipCodegen=true` as discussed above. ### Build Protobuf -The codegen plugin is C++ code and requires protobuf 3.17.2 or later. +The codegen plugin is C++ code and requires protobuf 3.19.1 or later. For Linux, Mac and MinGW: ``` -$ PROTOBUF_VERSION=3.17.2 +$ PROTOBUF_VERSION=3.19.1 $ curl -LO https://github.com/protocolbuffers/protobuf/releases/download/v$PROTOBUF_VERSION/protobuf-all-$PROTOBUF_VERSION.tar.gz $ tar xzf protobuf-all-$PROTOBUF_VERSION.tar.gz $ cd protobuf-$PROTOBUF_VERSION diff --git a/WORKSPACE b/WORKSPACE index dad4842a7a7..b6198573f28 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -4,17 +4,25 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") http_archive( name = "rules_jvm_external", - sha256 = "62133c125bf4109dfd9d2af64830208356ce4ef8b165a6ef15bbff7460b35c3a", - strip_prefix = "rules_jvm_external-3.0", - url = "https://github.com/bazelbuild/rules_jvm_external/archive/3.0.zip", + sha256 = "cd1a77b7b02e8e008439ca76fd34f5b07aecb8c752961f9640dea15e9e5ba1ca", + strip_prefix = "rules_jvm_external-4.2", + url = "https://github.com/bazelbuild/rules_jvm_external/archive/4.2.zip", ) load("@rules_jvm_external//:defs.bzl", "maven_install") load("//:repositories.bzl", "IO_GRPC_GRPC_JAVA_ARTIFACTS") load("//:repositories.bzl", "IO_GRPC_GRPC_JAVA_OVERRIDE_TARGETS") +load("//:repositories.bzl", "grpc_java_repositories") + +grpc_java_repositories() + +load("@com_google_protobuf//:protobuf_deps.bzl", "PROTOBUF_MAVEN_ARTIFACTS") +load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps") + +protobuf_deps() maven_install( - artifacts = IO_GRPC_GRPC_JAVA_ARTIFACTS, + artifacts = IO_GRPC_GRPC_JAVA_ARTIFACTS + PROTOBUF_MAVEN_ARTIFACTS, generate_compat_repositories = True, override_targets = IO_GRPC_GRPC_JAVA_OVERRIDE_TARGETS, repositories = [ @@ -25,11 +33,3 @@ maven_install( load("@maven//:compat.bzl", "compat_repositories") compat_repositories() - -load("//:repositories.bzl", "grpc_java_repositories") - -grpc_java_repositories() - -load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps") - -protobuf_deps() diff --git a/build.gradle b/build.gradle index 9d9e991bca5..57366e93861 100644 --- a/build.gradle +++ b/build.gradle @@ -55,9 +55,9 @@ subprojects { javaPluginPath = "$rootDir/compiler/build/exe/java_plugin/$protocPluginBaseName$exeSuffix" nettyVersion = '4.1.63.Final' - guavaVersion = '30.1-android' + guavaVersion = '30.1.1-android' googleauthVersion = '0.22.2' - protobufVersion = '3.17.2' + protobufVersion = '3.19.1' protocVersion = protobufVersion opencensusVersion = '0.28.0' autovalueVersion = '1.7.4' diff --git a/buildscripts/make_dependencies.bat b/buildscripts/make_dependencies.bat index 1622daaa16b..b8586e85832 100644 --- a/buildscripts/make_dependencies.bat +++ b/buildscripts/make_dependencies.bat @@ -1,4 +1,4 @@ -set PROTOBUF_VER=3.17.2 +set PROTOBUF_VER=3.19.1 set CMAKE_NAME=cmake-3.3.2-win32-x86 if not exist "protobuf-%PROTOBUF_VER%\cmake\build\Release\" ( diff --git a/buildscripts/make_dependencies.sh b/buildscripts/make_dependencies.sh index 927f1b4be28..7e132a91db0 100755 --- a/buildscripts/make_dependencies.sh +++ b/buildscripts/make_dependencies.sh @@ -3,7 +3,7 @@ # Build protoc set -evux -o pipefail -PROTOBUF_VERSION=3.17.2 +PROTOBUF_VERSION=3.19.1 # ARCH is x86_64 bit unless otherwise specified. ARCH="${ARCH:-x86_64}" diff --git a/examples/WORKSPACE b/examples/WORKSPACE index 36a002b4ee0..bd139ddd406 100644 --- a/examples/WORKSPACE +++ b/examples/WORKSPACE @@ -1,7 +1,9 @@ workspace(name = "examples") +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + # For released versions, use release tag: -# http_repository( +# http_archive( # name = "io_grpc_grpc_java", # sha256 = "", # strip_prefix = "grpc-java-", @@ -12,24 +14,30 @@ local_repository( path = "..", ) -load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") - http_archive( name = "rules_jvm_external", - sha256 = "62133c125bf4109dfd9d2af64830208356ce4ef8b165a6ef15bbff7460b35c3a", - strip_prefix = "rules_jvm_external-3.0", - url = "https://github.com/bazelbuild/rules_jvm_external/archive/3.0.zip", + sha256 = "cd1a77b7b02e8e008439ca76fd34f5b07aecb8c752961f9640dea15e9e5ba1ca", + strip_prefix = "rules_jvm_external-4.2", + url = "https://github.com/bazelbuild/rules_jvm_external/archive/4.2.zip", ) load("@rules_jvm_external//:defs.bzl", "maven_install") load("@io_grpc_grpc_java//:repositories.bzl", "IO_GRPC_GRPC_JAVA_ARTIFACTS") load("@io_grpc_grpc_java//:repositories.bzl", "IO_GRPC_GRPC_JAVA_OVERRIDE_TARGETS") +load("@io_grpc_grpc_java//:repositories.bzl", "grpc_java_repositories") + +grpc_java_repositories() + +load("@com_google_protobuf//:protobuf_deps.bzl", "PROTOBUF_MAVEN_ARTIFACTS") +load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps") + +protobuf_deps() maven_install( artifacts = [ "com.google.api.grpc:grpc-google-cloud-pubsub-v1:0.1.24", "com.google.api.grpc:proto-google-cloud-pubsub-v1:0.1.24", - ] + IO_GRPC_GRPC_JAVA_ARTIFACTS, + ] + IO_GRPC_GRPC_JAVA_ARTIFACTS + PROTOBUF_MAVEN_ARTIFACTS, generate_compat_repositories = True, override_targets = IO_GRPC_GRPC_JAVA_OVERRIDE_TARGETS, repositories = [ @@ -40,11 +48,3 @@ maven_install( load("@maven//:compat.bzl", "compat_repositories") compat_repositories() - -load("@io_grpc_grpc_java//:repositories.bzl", "grpc_java_repositories") - -grpc_java_repositories() - -load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps") - -protobuf_deps() diff --git a/examples/android/clientcache/app/build.gradle b/examples/android/clientcache/app/build.gradle index fbbcdd9b364..d3e2ccbb29d 100644 --- a/examples/android/clientcache/app/build.gradle +++ b/examples/android/clientcache/app/build.gradle @@ -32,7 +32,7 @@ android { } protobuf { - protoc { artifact = 'com.google.protobuf:protoc:3.17.2' } + protoc { artifact = 'com.google.protobuf:protoc:3.19.1' } plugins { grpc { artifact = 'io.grpc:protoc-gen-grpc-java:1.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION } diff --git a/examples/android/helloworld/app/build.gradle b/examples/android/helloworld/app/build.gradle index 0c71d91c0d2..d9cd0eb623a 100644 --- a/examples/android/helloworld/app/build.gradle +++ b/examples/android/helloworld/app/build.gradle @@ -30,7 +30,7 @@ android { } protobuf { - protoc { artifact = 'com.google.protobuf:protoc:3.17.2' } + protoc { artifact = 'com.google.protobuf:protoc:3.19.1' } plugins { grpc { artifact = 'io.grpc:protoc-gen-grpc-java:1.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION } diff --git a/examples/android/routeguide/app/build.gradle b/examples/android/routeguide/app/build.gradle index 09dc587850f..ce57964738a 100644 --- a/examples/android/routeguide/app/build.gradle +++ b/examples/android/routeguide/app/build.gradle @@ -30,7 +30,7 @@ android { } protobuf { - protoc { artifact = 'com.google.protobuf:protoc:3.17.2' } + protoc { artifact = 'com.google.protobuf:protoc:3.19.1' } plugins { grpc { artifact = 'io.grpc:protoc-gen-grpc-java:1.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION } diff --git a/examples/android/strictmode/app/build.gradle b/examples/android/strictmode/app/build.gradle index 9fb69f3f690..9e23b8684b5 100644 --- a/examples/android/strictmode/app/build.gradle +++ b/examples/android/strictmode/app/build.gradle @@ -31,7 +31,7 @@ android { } protobuf { - protoc { artifact = 'com.google.protobuf:protoc:3.17.2' } + protoc { artifact = 'com.google.protobuf:protoc:3.19.1' } plugins { grpc { artifact = 'io.grpc:protoc-gen-grpc-java:1.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION } diff --git a/examples/build.gradle b/examples/build.gradle index 4c88b826d38..fd4a32ea1c8 100644 --- a/examples/build.gradle +++ b/examples/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.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION -def protobufVersion = '3.17.2' +def protobufVersion = '3.19.1' def protocVersion = protobufVersion dependencies { diff --git a/examples/example-alts/build.gradle b/examples/example-alts/build.gradle index a0d726f6768..5d52d813134 100644 --- a/examples/example-alts/build.gradle +++ b/examples/example-alts/build.gradle @@ -24,7 +24,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.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION -def protocVersion = '3.17.2' +def protocVersion = '3.19.1' dependencies { // grpc-alts transitively depends on grpc-netty-shaded, grpc-protobuf, and grpc-stub diff --git a/examples/example-gauth/build.gradle b/examples/example-gauth/build.gradle index 2558469feb3..38e9480168e 100644 --- a/examples/example-gauth/build.gradle +++ b/examples/example-gauth/build.gradle @@ -24,7 +24,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.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION -def protobufVersion = '3.17.2' +def protobufVersion = '3.19.1' def protocVersion = protobufVersion diff --git a/examples/example-gauth/pom.xml b/examples/example-gauth/pom.xml index 67db6c7c9fb..4bcd2af9183 100644 --- a/examples/example-gauth/pom.xml +++ b/examples/example-gauth/pom.xml @@ -13,7 +13,7 @@ UTF-8 1.43.0-SNAPSHOT - 3.17.2 + 3.19.1 1.7 1.7 diff --git a/examples/example-hostname/build.gradle b/examples/example-hostname/build.gradle index 331d8309136..1d7e1104e2e 100644 --- a/examples/example-hostname/build.gradle +++ b/examples/example-hostname/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.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION -def protobufVersion = '3.17.2' +def protobufVersion = '3.19.1' dependencies { implementation "io.grpc:grpc-protobuf:${grpcVersion}" diff --git a/examples/example-hostname/pom.xml b/examples/example-hostname/pom.xml index d4625193bf8..72759600b24 100644 --- a/examples/example-hostname/pom.xml +++ b/examples/example-hostname/pom.xml @@ -13,7 +13,7 @@ UTF-8 1.43.0-SNAPSHOT - 3.17.2 + 3.19.1 1.7 1.7 diff --git a/examples/example-jwt-auth/build.gradle b/examples/example-jwt-auth/build.gradle index 94ae0610a21..f0391a94c83 100644 --- a/examples/example-jwt-auth/build.gradle +++ b/examples/example-jwt-auth/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.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION -def protobufVersion = '3.17.2' +def protobufVersion = '3.19.1' def protocVersion = protobufVersion dependencies { diff --git a/examples/example-jwt-auth/pom.xml b/examples/example-jwt-auth/pom.xml index 9ac3aefe34e..4b95f74ca11 100644 --- a/examples/example-jwt-auth/pom.xml +++ b/examples/example-jwt-auth/pom.xml @@ -14,8 +14,8 @@ UTF-8 1.43.0-SNAPSHOT - 3.17.2 - 3.17.2 + 3.19.1 + 3.19.1 1.7 1.7 diff --git a/examples/example-tls/build.gradle b/examples/example-tls/build.gradle index 8a8ceb371af..5d1c1b3ce1d 100644 --- a/examples/example-tls/build.gradle +++ b/examples/example-tls/build.gradle @@ -24,7 +24,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.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION -def protocVersion = '3.17.2' +def protocVersion = '3.19.1' dependencies { implementation "io.grpc:grpc-protobuf:${grpcVersion}" diff --git a/examples/example-tls/pom.xml b/examples/example-tls/pom.xml index 60d632bbb87..36492f6acfe 100644 --- a/examples/example-tls/pom.xml +++ b/examples/example-tls/pom.xml @@ -13,7 +13,7 @@ UTF-8 1.43.0-SNAPSHOT - 3.17.2 + 3.19.1 2.0.34.Final 1.7 diff --git a/examples/example-xds/build.gradle b/examples/example-xds/build.gradle index d00beaa06bb..69dde002baf 100644 --- a/examples/example-xds/build.gradle +++ b/examples/example-xds/build.gradle @@ -24,7 +24,7 @@ targetCompatibility = 1.7 // updating the version in our release process. def grpcVersion = '1.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION def nettyTcNativeVersion = '2.0.31.Final' -def protocVersion = '3.17.2' +def protocVersion = '3.19.1' dependencies { implementation "io.grpc:grpc-protobuf:${grpcVersion}" diff --git a/examples/pom.xml b/examples/pom.xml index 0704ce80d88..2b04cf057f1 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -13,8 +13,8 @@ UTF-8 1.43.0-SNAPSHOT - 3.17.2 - 3.17.2 + 3.19.1 + 3.19.1 1.7 1.7 diff --git a/repositories.bzl b/repositories.bzl index 3222e15ff65..712e34fded0 100644 --- a/repositories.bzl +++ b/repositories.bzl @@ -20,7 +20,7 @@ IO_GRPC_GRPC_JAVA_ARTIFACTS = [ "com.google.auto.value:auto-value-annotations:1.7.4", "com.google.errorprone:error_prone_annotations:2.9.0", "com.google.guava:failureaccess:1.0.1", - "com.google.guava:guava:30.1-android", + "com.google.guava:guava:30.1.1-android", "com.google.j2objc:j2objc-annotations:1.3", "com.google.truth:truth:1.0.1", "com.squareup.okhttp:okhttp:2.7.4", @@ -92,37 +92,24 @@ def grpc_java_repositories(): if not native.existing_rule("io_grpc_grpc_proto"): io_grpc_grpc_proto() - native.bind( - name = "guava", - actual = "@com_google_guava_guava//jar", - ) - native.bind( - name = "gson", - actual = "@com_google_code_gson_gson//jar", - ) - native.bind( - name = "error_prone_annotations", - actual = "@com_google_errorprone_error_prone_annotations//jar", - ) - def com_google_protobuf(): # proto_library rules implicitly depend on @com_google_protobuf//:protoc, # which is the proto-compiler. # This statement defines the @com_google_protobuf repo. http_archive( name = "com_google_protobuf", - sha256 = "f6042eef01551cee4c663a11c3f429c06360a1f51daa9f4772bf3f13d24cde1f", - strip_prefix = "protobuf-3.17.2", - urls = ["https://github.com/protocolbuffers/protobuf/archive/v3.17.2.zip"], + sha256 = "25f1292d4ea6666f460a2a30038eef121e6c3937ae0f61d610611dfb14b0bd32", + strip_prefix = "protobuf-3.19.1", + urls = ["https://github.com/protocolbuffers/protobuf/archive/v3.19.1.zip"], ) def com_google_protobuf_javalite(): # java_lite_proto_library rules implicitly depend on @com_google_protobuf_javalite http_archive( name = "com_google_protobuf_javalite", - sha256 = "f6042eef01551cee4c663a11c3f429c06360a1f51daa9f4772bf3f13d24cde1f", - strip_prefix = "protobuf-3.17.2", - urls = ["https://github.com/protocolbuffers/protobuf/archive/v3.17.2.zip"], + sha256 = "25f1292d4ea6666f460a2a30038eef121e6c3937ae0f61d610611dfb14b0bd32", + strip_prefix = "protobuf-3.19.1", + urls = ["https://github.com/protocolbuffers/protobuf/archive/v3.19.1.zip"], ) def io_grpc_grpc_proto(): From 02711b41ac707876f60bb1dce3dc66896dd1bef9 Mon Sep 17 00:00:00 2001 From: "Penn (Dapeng) Zhang" Date: Tue, 14 Dec 2021 12:05:07 -0500 Subject: [PATCH 49/50] Update README etc to reference 1.43.0 --- README.md | 36 ++++++++++++------------ cronet/README.md | 2 +- documentation/android-channel-builder.md | 4 +-- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index d11c2850805..853b37e8c6a 100644 --- a/README.md +++ b/README.md @@ -31,8 +31,8 @@ For a guided tour, take a look at the [quick start guide](https://grpc.io/docs/languages/java/quickstart) or the more explanatory [gRPC basics](https://grpc.io/docs/languages/java/basics). -The [examples](https://github.com/grpc/grpc-java/tree/v1.42.1/examples) and the -[Android example](https://github.com/grpc/grpc-java/tree/v1.42.1/examples/android) +The [examples](https://github.com/grpc/grpc-java/tree/v1.43.0/examples) and the +[Android example](https://github.com/grpc/grpc-java/tree/v1.43.0/examples/android) are standalone projects that showcase the usage of gRPC. Download @@ -43,17 +43,17 @@ Download [the JARs][]. Or for Maven with non-Android, add to your `pom.xml`: io.grpc grpc-netty-shaded - 1.42.1 + 1.43.0 io.grpc grpc-protobuf - 1.42.1 + 1.43.0 io.grpc grpc-stub - 1.42.1 + 1.43.0 org.apache.tomcat @@ -65,23 +65,23 @@ Download [the JARs][]. Or for Maven with non-Android, add to your `pom.xml`: Or for Gradle with non-Android, add to your dependencies: ```gradle -implementation 'io.grpc:grpc-netty-shaded:1.42.1' -implementation 'io.grpc:grpc-protobuf:1.42.1' -implementation 'io.grpc:grpc-stub:1.42.1' +implementation 'io.grpc:grpc-netty-shaded:1.43.0' +implementation 'io.grpc:grpc-protobuf:1.43.0' +implementation 'io.grpc:grpc-stub:1.43.0' compileOnly 'org.apache.tomcat:annotations-api:6.0.53' // necessary for Java 9+ ``` For Android client, use `grpc-okhttp` instead of `grpc-netty-shaded` and `grpc-protobuf-lite` instead of `grpc-protobuf`: ```gradle -implementation 'io.grpc:grpc-okhttp:1.42.1' -implementation 'io.grpc:grpc-protobuf-lite:1.42.1' -implementation 'io.grpc:grpc-stub:1.42.1' +implementation 'io.grpc:grpc-okhttp:1.43.0' +implementation 'io.grpc:grpc-protobuf-lite:1.43.0' +implementation 'io.grpc:grpc-stub:1.43.0' compileOnly 'org.apache.tomcat:annotations-api:6.0.53' // necessary for Java 9+ ``` [the JARs]: -https://search.maven.org/search?q=g:io.grpc%20AND%20v:1.42.1 +https://search.maven.org/search?q=g:io.grpc%20AND%20v:1.43.0 Development snapshots are available in [Sonatypes's snapshot repository](https://oss.sonatype.org/content/repositories/snapshots/). @@ -111,9 +111,9 @@ For protobuf-based codegen integrated with the Maven build system, you can use protobuf-maven-plugin 0.6.1 - com.google.protobuf:protoc:3.17.2:exe:${os.detected.classifier} + com.google.protobuf:protoc:3.19.1:exe:${os.detected.classifier} grpc-java - io.grpc:protoc-gen-grpc-java:1.42.1:exe:${os.detected.classifier} + io.grpc:protoc-gen-grpc-java:1.43.0:exe:${os.detected.classifier} @@ -139,11 +139,11 @@ plugins { protobuf { protoc { - artifact = "com.google.protobuf:protoc:3.17.2" + artifact = "com.google.protobuf:protoc:3.19.1" } plugins { grpc { - artifact = 'io.grpc:protoc-gen-grpc-java:1.42.1' + artifact = 'io.grpc:protoc-gen-grpc-java:1.43.0' } } generateProtoTasks { @@ -172,11 +172,11 @@ plugins { protobuf { protoc { - artifact = "com.google.protobuf:protoc:3.17.2" + artifact = "com.google.protobuf:protoc:3.19.1" } plugins { grpc { - artifact = 'io.grpc:protoc-gen-grpc-java:1.42.1' + artifact = 'io.grpc:protoc-gen-grpc-java:1.43.0' } } generateProtoTasks { diff --git a/cronet/README.md b/cronet/README.md index a682c5bcee8..e7705acc3de 100644 --- a/cronet/README.md +++ b/cronet/README.md @@ -26,7 +26,7 @@ In your app module's `build.gradle` file, include a dependency on both `grpc-cro Google Play Services Client Library for Cronet ``` -implementation 'io.grpc:grpc-cronet:1.42.1' +implementation 'io.grpc:grpc-cronet:1.43.0' implementation 'com.google.android.gms:play-services-cronet:16.0.0' ``` diff --git a/documentation/android-channel-builder.md b/documentation/android-channel-builder.md index d9541c56623..3ebbf9522a4 100644 --- a/documentation/android-channel-builder.md +++ b/documentation/android-channel-builder.md @@ -36,8 +36,8 @@ In your `build.gradle` file, include a dependency on both `grpc-android` and `grpc-okhttp`: ``` -implementation 'io.grpc:grpc-android:1.42.1' -implementation 'io.grpc:grpc-okhttp:1.42.1' +implementation 'io.grpc:grpc-android:1.43.0' +implementation 'io.grpc:grpc-okhttp:1.43.0' ``` You also need permission to access the device's network state in your From 977145f1cec1e31971834d90d85b547020e75eee Mon Sep 17 00:00:00 2001 From: "Penn (Dapeng) Zhang" Date: Tue, 14 Dec 2021 12:18:01 -0500 Subject: [PATCH 50/50] Bump version to 1.43.0 --- 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 ++-- examples/example-tls/build.gradle | 2 +- examples/example-tls/pom.xml | 4 ++-- examples/example-xds/build.gradle | 2 +- examples/pom.xml | 4 ++-- 22 files changed, 40 insertions(+), 40 deletions(-) diff --git a/build.gradle b/build.gradle index 57366e93861..02ebb792e23 100644 --- a/build.gradle +++ b/build.gradle @@ -18,7 +18,7 @@ subprojects { apply plugin: "net.ltgt.errorprone" group = "io.grpc" - version = "1.43.0-SNAPSHOT" // CURRENT_GRPC_VERSION + version = "1.43.0" // 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 98dcbf87587..b54976b9623 100644 --- a/compiler/src/test/golden/TestDeprecatedService.java.txt +++ b/compiler/src/test/golden/TestDeprecatedService.java.txt @@ -8,7 +8,7 @@ import static io.grpc.MethodDescriptor.generateFullMethodName; * */ @javax.annotation.Generated( - value = "by gRPC proto compiler (version 1.43.0-SNAPSHOT)", + value = "by gRPC proto compiler (version 1.43.0)", comments = "Source: grpc/testing/compiler/test.proto") @io.grpc.stub.annotations.GrpcGenerated @java.lang.Deprecated diff --git a/compiler/src/test/golden/TestService.java.txt b/compiler/src/test/golden/TestService.java.txt index 49938bd9345..f54aa6e037c 100644 --- a/compiler/src/test/golden/TestService.java.txt +++ b/compiler/src/test/golden/TestService.java.txt @@ -8,7 +8,7 @@ import static io.grpc.MethodDescriptor.generateFullMethodName; * */ @javax.annotation.Generated( - value = "by gRPC proto compiler (version 1.43.0-SNAPSHOT)", + value = "by gRPC proto compiler (version 1.43.0)", comments = "Source: grpc/testing/compiler/test.proto") @io.grpc.stub.annotations.GrpcGenerated public final class TestServiceGrpc { diff --git a/compiler/src/testLite/golden/TestDeprecatedService.java.txt b/compiler/src/testLite/golden/TestDeprecatedService.java.txt index fdd980a554e..f7e381181c1 100644 --- a/compiler/src/testLite/golden/TestDeprecatedService.java.txt +++ b/compiler/src/testLite/golden/TestDeprecatedService.java.txt @@ -8,7 +8,7 @@ import static io.grpc.MethodDescriptor.generateFullMethodName; * */ @javax.annotation.Generated( - value = "by gRPC proto compiler (version 1.43.0-SNAPSHOT)", + value = "by gRPC proto compiler (version 1.43.0)", comments = "Source: grpc/testing/compiler/test.proto") @io.grpc.stub.annotations.GrpcGenerated @java.lang.Deprecated diff --git a/compiler/src/testLite/golden/TestService.java.txt b/compiler/src/testLite/golden/TestService.java.txt index e13628ac66f..4fa8ca2bcad 100644 --- a/compiler/src/testLite/golden/TestService.java.txt +++ b/compiler/src/testLite/golden/TestService.java.txt @@ -8,7 +8,7 @@ import static io.grpc.MethodDescriptor.generateFullMethodName; * */ @javax.annotation.Generated( - value = "by gRPC proto compiler (version 1.43.0-SNAPSHOT)", + value = "by gRPC proto compiler (version 1.43.0)", comments = "Source: grpc/testing/compiler/test.proto") @io.grpc.stub.annotations.GrpcGenerated 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 6a7b93baf06..f19d92dee66 100644 --- a/core/src/main/java/io/grpc/internal/GrpcUtil.java +++ b/core/src/main/java/io/grpc/internal/GrpcUtil.java @@ -205,7 +205,7 @@ public byte[] parseAsciiString(byte[] serialized) { public static final Splitter ACCEPT_ENCODING_SPLITTER = Splitter.on(',').trimResults(); - private static final String IMPLEMENTATION_VERSION = "1.43.0-SNAPSHOT"; // CURRENT_GRPC_VERSION + private static final String IMPLEMENTATION_VERSION = "1.43.0"; // 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 d3e2ccbb29d..14110cd85c4 100644 --- a/examples/android/clientcache/app/build.gradle +++ b/examples/android/clientcache/app/build.gradle @@ -34,7 +34,7 @@ android { protobuf { protoc { artifact = 'com.google.protobuf:protoc:3.19.1' } plugins { - grpc { artifact = 'io.grpc:protoc-gen-grpc-java:1.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION + grpc { artifact = 'io.grpc:protoc-gen-grpc-java:1.43.0' // CURRENT_GRPC_VERSION } } generateProtoTasks { @@ -54,12 +54,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.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION - implementation 'io.grpc:grpc-protobuf-lite:1.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION - implementation 'io.grpc:grpc-stub:1.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION + implementation 'io.grpc:grpc-okhttp:1.43.0' // CURRENT_GRPC_VERSION + implementation 'io.grpc:grpc-protobuf-lite:1.43.0' // CURRENT_GRPC_VERSION + implementation 'io.grpc:grpc-stub:1.43.0' // CURRENT_GRPC_VERSION implementation 'org.apache.tomcat:annotations-api:6.0.53' testImplementation 'junit:junit:4.12' testImplementation 'com.google.truth:truth:1.0.1' - testImplementation 'io.grpc:grpc-testing:1.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION + testImplementation 'io.grpc:grpc-testing:1.43.0' // CURRENT_GRPC_VERSION } diff --git a/examples/android/helloworld/app/build.gradle b/examples/android/helloworld/app/build.gradle index d9cd0eb623a..e869ffeed65 100644 --- a/examples/android/helloworld/app/build.gradle +++ b/examples/android/helloworld/app/build.gradle @@ -32,7 +32,7 @@ android { protobuf { protoc { artifact = 'com.google.protobuf:protoc:3.19.1' } plugins { - grpc { artifact = 'io.grpc:protoc-gen-grpc-java:1.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION + grpc { artifact = 'io.grpc:protoc-gen-grpc-java:1.43.0' // CURRENT_GRPC_VERSION } } generateProtoTasks { @@ -52,8 +52,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.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION - implementation 'io.grpc:grpc-protobuf-lite:1.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION - implementation 'io.grpc:grpc-stub:1.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION + implementation 'io.grpc:grpc-okhttp:1.43.0' // CURRENT_GRPC_VERSION + implementation 'io.grpc:grpc-protobuf-lite:1.43.0' // CURRENT_GRPC_VERSION + implementation 'io.grpc:grpc-stub:1.43.0' // CURRENT_GRPC_VERSION implementation 'org.apache.tomcat:annotations-api:6.0.53' } diff --git a/examples/android/routeguide/app/build.gradle b/examples/android/routeguide/app/build.gradle index ce57964738a..3ea4223db27 100644 --- a/examples/android/routeguide/app/build.gradle +++ b/examples/android/routeguide/app/build.gradle @@ -32,7 +32,7 @@ android { protobuf { protoc { artifact = 'com.google.protobuf:protoc:3.19.1' } plugins { - grpc { artifact = 'io.grpc:protoc-gen-grpc-java:1.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION + grpc { artifact = 'io.grpc:protoc-gen-grpc-java:1.43.0' // CURRENT_GRPC_VERSION } } generateProtoTasks { @@ -52,8 +52,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.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION - implementation 'io.grpc:grpc-protobuf-lite:1.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION - implementation 'io.grpc:grpc-stub:1.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION + implementation 'io.grpc:grpc-okhttp:1.43.0' // CURRENT_GRPC_VERSION + implementation 'io.grpc:grpc-protobuf-lite:1.43.0' // CURRENT_GRPC_VERSION + implementation 'io.grpc:grpc-stub:1.43.0' // CURRENT_GRPC_VERSION implementation 'org.apache.tomcat:annotations-api:6.0.53' } diff --git a/examples/android/strictmode/app/build.gradle b/examples/android/strictmode/app/build.gradle index 9e23b8684b5..4a6893192e6 100644 --- a/examples/android/strictmode/app/build.gradle +++ b/examples/android/strictmode/app/build.gradle @@ -33,7 +33,7 @@ android { protobuf { protoc { artifact = 'com.google.protobuf:protoc:3.19.1' } plugins { - grpc { artifact = 'io.grpc:protoc-gen-grpc-java:1.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION + grpc { artifact = 'io.grpc:protoc-gen-grpc-java:1.43.0' // CURRENT_GRPC_VERSION } } generateProtoTasks { @@ -53,8 +53,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.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION - implementation 'io.grpc:grpc-protobuf-lite:1.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION - implementation 'io.grpc:grpc-stub:1.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION + implementation 'io.grpc:grpc-okhttp:1.43.0' // CURRENT_GRPC_VERSION + implementation 'io.grpc:grpc-protobuf-lite:1.43.0' // CURRENT_GRPC_VERSION + implementation 'io.grpc:grpc-stub:1.43.0' // CURRENT_GRPC_VERSION implementation 'org.apache.tomcat:annotations-api:6.0.53' } diff --git a/examples/build.gradle b/examples/build.gradle index fd4a32ea1c8..05dc01743bb 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.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION +def grpcVersion = '1.43.0' // CURRENT_GRPC_VERSION def protobufVersion = '3.19.1' def protocVersion = protobufVersion diff --git a/examples/example-alts/build.gradle b/examples/example-alts/build.gradle index 5d52d813134..27a926c0d7f 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.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION +def grpcVersion = '1.43.0' // CURRENT_GRPC_VERSION def protocVersion = '3.19.1' dependencies { diff --git a/examples/example-gauth/build.gradle b/examples/example-gauth/build.gradle index 38e9480168e..0ed445ff5a4 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.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION +def grpcVersion = '1.43.0' // CURRENT_GRPC_VERSION def protobufVersion = '3.19.1' def protocVersion = protobufVersion diff --git a/examples/example-gauth/pom.xml b/examples/example-gauth/pom.xml index 4bcd2af9183..39436678a63 100644 --- a/examples/example-gauth/pom.xml +++ b/examples/example-gauth/pom.xml @@ -6,13 +6,13 @@ jar - 1.43.0-SNAPSHOT + 1.43.0 example-gauth https://github.com/grpc/grpc-java UTF-8 - 1.43.0-SNAPSHOT + 1.43.0 3.19.1 1.7 diff --git a/examples/example-hostname/build.gradle b/examples/example-hostname/build.gradle index 1d7e1104e2e..f6727f9d6a0 100644 --- a/examples/example-hostname/build.gradle +++ b/examples/example-hostname/build.gradle @@ -21,7 +21,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.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION +def grpcVersion = '1.43.0' // CURRENT_GRPC_VERSION def protobufVersion = '3.19.1' dependencies { diff --git a/examples/example-hostname/pom.xml b/examples/example-hostname/pom.xml index 72759600b24..989e275e986 100644 --- a/examples/example-hostname/pom.xml +++ b/examples/example-hostname/pom.xml @@ -6,13 +6,13 @@ jar - 1.43.0-SNAPSHOT + 1.43.0 example-hostname https://github.com/grpc/grpc-java UTF-8 - 1.43.0-SNAPSHOT + 1.43.0 3.19.1 1.7 diff --git a/examples/example-jwt-auth/build.gradle b/examples/example-jwt-auth/build.gradle index f0391a94c83..621fff1d667 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.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION +def grpcVersion = '1.43.0' // CURRENT_GRPC_VERSION def protobufVersion = '3.19.1' def protocVersion = protobufVersion diff --git a/examples/example-jwt-auth/pom.xml b/examples/example-jwt-auth/pom.xml index 4b95f74ca11..cd5673797c1 100644 --- a/examples/example-jwt-auth/pom.xml +++ b/examples/example-jwt-auth/pom.xml @@ -7,13 +7,13 @@ jar - 1.43.0-SNAPSHOT + 1.43.0 example-jwt-auth https://github.com/grpc/grpc-java UTF-8 - 1.43.0-SNAPSHOT + 1.43.0 3.19.1 3.19.1 diff --git a/examples/example-tls/build.gradle b/examples/example-tls/build.gradle index 5d1c1b3ce1d..884eae41b02 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.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION +def grpcVersion = '1.43.0' // CURRENT_GRPC_VERSION def protocVersion = '3.19.1' dependencies { diff --git a/examples/example-tls/pom.xml b/examples/example-tls/pom.xml index 36492f6acfe..1668dc4acf0 100644 --- a/examples/example-tls/pom.xml +++ b/examples/example-tls/pom.xml @@ -6,13 +6,13 @@ jar - 1.43.0-SNAPSHOT + 1.43.0 example-tls https://github.com/grpc/grpc-java UTF-8 - 1.43.0-SNAPSHOT + 1.43.0 3.19.1 2.0.34.Final diff --git a/examples/example-xds/build.gradle b/examples/example-xds/build.gradle index 69dde002baf..50953d3e06a 100644 --- a/examples/example-xds/build.gradle +++ b/examples/example-xds/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.43.0-SNAPSHOT' // CURRENT_GRPC_VERSION +def grpcVersion = '1.43.0' // CURRENT_GRPC_VERSION def nettyTcNativeVersion = '2.0.31.Final' def protocVersion = '3.19.1' diff --git a/examples/pom.xml b/examples/pom.xml index 2b04cf057f1..ab5ad3d9b2b 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -6,13 +6,13 @@ jar - 1.43.0-SNAPSHOT + 1.43.0 examples https://github.com/grpc/grpc-java UTF-8 - 1.43.0-SNAPSHOT + 1.43.0 3.19.1 3.19.1