diff --git a/build.gradle b/build.gradle index 46f96e11bd..cc4b30e8f2 100644 --- a/build.gradle +++ b/build.gradle @@ -4,6 +4,7 @@ plugins { id "com.google.osdetector" apply false id "me.champeau.gradle.japicmp" apply false id "net.ltgt.errorprone" apply false + id 'com.google.cloud.tools.jib' apply false } import net.ltgt.gradle.errorprone.CheckSeverity diff --git a/istio-interop-testing/build.gradle b/istio-interop-testing/build.gradle new file mode 100644 index 0000000000..edccac9454 --- /dev/null +++ b/istio-interop-testing/build.gradle @@ -0,0 +1,68 @@ +plugins { + id "application" + id "java" + + id "com.google.protobuf" + id 'com.google.cloud.tools.jib' +} + +description = "gRPC: Istio Interop testing" + +configurations { + alpnagent +} + +evaluationDependsOn(project(':grpc-context').path) + +dependencies { + implementation project(':grpc-core'), + project(':grpc-netty'), + project(':grpc-protobuf'), + project(':grpc-services'), + project(':grpc-stub'), + project(':grpc-testing') + + compileOnly libraries.javax.annotation + + runtimeOnly libraries.netty.tcnative, + libraries.netty.tcnative.classes + testImplementation project(':grpc-context').sourceSets.test.output, + project(':grpc-api').sourceSets.test.output, + project(':grpc-core').sourceSets.test.output, + libraries.mockito.core, + libraries.junit, + libraries.truth + alpnagent libraries.jetty.alpn.agent +} + +sourceSets { + main { + proto { + srcDir 'third_party/istio/src/main/proto' + } + } +} + +configureProtoCompilation() + +import net.ltgt.gradle.errorprone.CheckSeverity + +compileJava { + // This isn't a library; it can use beta APIs + options.errorprone.check("BetaApi", CheckSeverity.OFF) +} + + +// For releasing to Docker Hub +jib { + from.image = "gcr.io/distroless/java:8" + container { + ports = ['50051'] + mainClass="io.grpc.testing.istio.EchoTestServer" + } + outputPaths { + tar = 'build/istio-echo-server.tar' + digest = 'build/istio-echo-server.digest' + imageId = 'build/istio-echo-server.id' + } +} diff --git a/istio-interop-testing/src/generated/main/grpc/io/istio/test/EchoTestServiceGrpc.java b/istio-interop-testing/src/generated/main/grpc/io/istio/test/EchoTestServiceGrpc.java new file mode 100644 index 0000000000..1c71469e01 --- /dev/null +++ b/istio-interop-testing/src/generated/main/grpc/io/istio/test/EchoTestServiceGrpc.java @@ -0,0 +1,350 @@ +package io.istio.test; + +import static io.grpc.MethodDescriptor.generateFullMethodName; + +/** + */ +@javax.annotation.Generated( + value = "by gRPC proto compiler", + comments = "Source: test/echo/proto/echo.proto") +@io.grpc.stub.annotations.GrpcGenerated +public final class EchoTestServiceGrpc { + + private EchoTestServiceGrpc() {} + + public static final String SERVICE_NAME = "proto.EchoTestService"; + + // Static method descriptors that strictly reflect the proto. + private static volatile io.grpc.MethodDescriptor getEchoMethod; + + @io.grpc.stub.annotations.RpcMethod( + fullMethodName = SERVICE_NAME + '/' + "Echo", + requestType = io.istio.test.Echo.EchoRequest.class, + responseType = io.istio.test.Echo.EchoResponse.class, + methodType = io.grpc.MethodDescriptor.MethodType.UNARY) + public static io.grpc.MethodDescriptor getEchoMethod() { + io.grpc.MethodDescriptor getEchoMethod; + if ((getEchoMethod = EchoTestServiceGrpc.getEchoMethod) == null) { + synchronized (EchoTestServiceGrpc.class) { + if ((getEchoMethod = EchoTestServiceGrpc.getEchoMethod) == null) { + EchoTestServiceGrpc.getEchoMethod = getEchoMethod = + io.grpc.MethodDescriptor.newBuilder() + .setType(io.grpc.MethodDescriptor.MethodType.UNARY) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "Echo")) + .setSampledToLocalTracing(true) + .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + io.istio.test.Echo.EchoRequest.getDefaultInstance())) + .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + io.istio.test.Echo.EchoResponse.getDefaultInstance())) + .setSchemaDescriptor(new EchoTestServiceMethodDescriptorSupplier("Echo")) + .build(); + } + } + } + return getEchoMethod; + } + + private static volatile io.grpc.MethodDescriptor getForwardEchoMethod; + + @io.grpc.stub.annotations.RpcMethod( + fullMethodName = SERVICE_NAME + '/' + "ForwardEcho", + requestType = io.istio.test.Echo.ForwardEchoRequest.class, + responseType = io.istio.test.Echo.ForwardEchoResponse.class, + methodType = io.grpc.MethodDescriptor.MethodType.UNARY) + public static io.grpc.MethodDescriptor getForwardEchoMethod() { + io.grpc.MethodDescriptor getForwardEchoMethod; + if ((getForwardEchoMethod = EchoTestServiceGrpc.getForwardEchoMethod) == null) { + synchronized (EchoTestServiceGrpc.class) { + if ((getForwardEchoMethod = EchoTestServiceGrpc.getForwardEchoMethod) == null) { + EchoTestServiceGrpc.getForwardEchoMethod = getForwardEchoMethod = + io.grpc.MethodDescriptor.newBuilder() + .setType(io.grpc.MethodDescriptor.MethodType.UNARY) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "ForwardEcho")) + .setSampledToLocalTracing(true) + .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + io.istio.test.Echo.ForwardEchoRequest.getDefaultInstance())) + .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + io.istio.test.Echo.ForwardEchoResponse.getDefaultInstance())) + .setSchemaDescriptor(new EchoTestServiceMethodDescriptorSupplier("ForwardEcho")) + .build(); + } + } + } + return getForwardEchoMethod; + } + + /** + * Creates a new async stub that supports all call types for the service + */ + public static EchoTestServiceStub newStub(io.grpc.Channel channel) { + io.grpc.stub.AbstractStub.StubFactory factory = + new io.grpc.stub.AbstractStub.StubFactory() { + @java.lang.Override + public EchoTestServiceStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new EchoTestServiceStub(channel, callOptions); + } + }; + return EchoTestServiceStub.newStub(factory, channel); + } + + /** + * Creates a new blocking-style stub that supports unary and streaming output calls on the service + */ + public static EchoTestServiceBlockingStub newBlockingStub( + io.grpc.Channel channel) { + io.grpc.stub.AbstractStub.StubFactory factory = + new io.grpc.stub.AbstractStub.StubFactory() { + @java.lang.Override + public EchoTestServiceBlockingStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new EchoTestServiceBlockingStub(channel, callOptions); + } + }; + return EchoTestServiceBlockingStub.newStub(factory, channel); + } + + /** + * Creates a new ListenableFuture-style stub that supports unary calls on the service + */ + public static EchoTestServiceFutureStub newFutureStub( + io.grpc.Channel channel) { + io.grpc.stub.AbstractStub.StubFactory factory = + new io.grpc.stub.AbstractStub.StubFactory() { + @java.lang.Override + public EchoTestServiceFutureStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new EchoTestServiceFutureStub(channel, callOptions); + } + }; + return EchoTestServiceFutureStub.newStub(factory, channel); + } + + /** + */ + public static abstract class EchoTestServiceImplBase implements io.grpc.BindableService { + + /** + */ + public void echo(io.istio.test.Echo.EchoRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getEchoMethod(), responseObserver); + } + + /** + */ + public void forwardEcho(io.istio.test.Echo.ForwardEchoRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getForwardEchoMethod(), responseObserver); + } + + @java.lang.Override public final io.grpc.ServerServiceDefinition bindService() { + return io.grpc.ServerServiceDefinition.builder(getServiceDescriptor()) + .addMethod( + getEchoMethod(), + io.grpc.stub.ServerCalls.asyncUnaryCall( + new MethodHandlers< + io.istio.test.Echo.EchoRequest, + io.istio.test.Echo.EchoResponse>( + this, METHODID_ECHO))) + .addMethod( + getForwardEchoMethod(), + io.grpc.stub.ServerCalls.asyncUnaryCall( + new MethodHandlers< + io.istio.test.Echo.ForwardEchoRequest, + io.istio.test.Echo.ForwardEchoResponse>( + this, METHODID_FORWARD_ECHO))) + .build(); + } + } + + /** + */ + public static final class EchoTestServiceStub extends io.grpc.stub.AbstractAsyncStub { + private EchoTestServiceStub( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + super(channel, callOptions); + } + + @java.lang.Override + protected EchoTestServiceStub build( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new EchoTestServiceStub(channel, callOptions); + } + + /** + */ + public void echo(io.istio.test.Echo.EchoRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ClientCalls.asyncUnaryCall( + getChannel().newCall(getEchoMethod(), getCallOptions()), request, responseObserver); + } + + /** + */ + public void forwardEcho(io.istio.test.Echo.ForwardEchoRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ClientCalls.asyncUnaryCall( + getChannel().newCall(getForwardEchoMethod(), getCallOptions()), request, responseObserver); + } + } + + /** + */ + public static final class EchoTestServiceBlockingStub extends io.grpc.stub.AbstractBlockingStub { + private EchoTestServiceBlockingStub( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + super(channel, callOptions); + } + + @java.lang.Override + protected EchoTestServiceBlockingStub build( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new EchoTestServiceBlockingStub(channel, callOptions); + } + + /** + */ + public io.istio.test.Echo.EchoResponse echo(io.istio.test.Echo.EchoRequest request) { + return io.grpc.stub.ClientCalls.blockingUnaryCall( + getChannel(), getEchoMethod(), getCallOptions(), request); + } + + /** + */ + public io.istio.test.Echo.ForwardEchoResponse forwardEcho(io.istio.test.Echo.ForwardEchoRequest request) { + return io.grpc.stub.ClientCalls.blockingUnaryCall( + getChannel(), getForwardEchoMethod(), getCallOptions(), request); + } + } + + /** + */ + public static final class EchoTestServiceFutureStub extends io.grpc.stub.AbstractFutureStub { + private EchoTestServiceFutureStub( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + super(channel, callOptions); + } + + @java.lang.Override + protected EchoTestServiceFutureStub build( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new EchoTestServiceFutureStub(channel, callOptions); + } + + /** + */ + public com.google.common.util.concurrent.ListenableFuture echo( + io.istio.test.Echo.EchoRequest request) { + return io.grpc.stub.ClientCalls.futureUnaryCall( + getChannel().newCall(getEchoMethod(), getCallOptions()), request); + } + + /** + */ + public com.google.common.util.concurrent.ListenableFuture forwardEcho( + io.istio.test.Echo.ForwardEchoRequest request) { + return io.grpc.stub.ClientCalls.futureUnaryCall( + getChannel().newCall(getForwardEchoMethod(), getCallOptions()), request); + } + } + + private static final int METHODID_ECHO = 0; + private static final int METHODID_FORWARD_ECHO = 1; + + private static final class MethodHandlers implements + io.grpc.stub.ServerCalls.UnaryMethod, + io.grpc.stub.ServerCalls.ServerStreamingMethod, + io.grpc.stub.ServerCalls.ClientStreamingMethod, + io.grpc.stub.ServerCalls.BidiStreamingMethod { + private final EchoTestServiceImplBase serviceImpl; + private final int methodId; + + MethodHandlers(EchoTestServiceImplBase serviceImpl, int methodId) { + this.serviceImpl = serviceImpl; + this.methodId = methodId; + } + + @java.lang.Override + @java.lang.SuppressWarnings("unchecked") + public void invoke(Req request, io.grpc.stub.StreamObserver responseObserver) { + switch (methodId) { + case METHODID_ECHO: + serviceImpl.echo((io.istio.test.Echo.EchoRequest) request, + (io.grpc.stub.StreamObserver) responseObserver); + break; + case METHODID_FORWARD_ECHO: + serviceImpl.forwardEcho((io.istio.test.Echo.ForwardEchoRequest) request, + (io.grpc.stub.StreamObserver) responseObserver); + break; + default: + throw new AssertionError(); + } + } + + @java.lang.Override + @java.lang.SuppressWarnings("unchecked") + public io.grpc.stub.StreamObserver invoke( + io.grpc.stub.StreamObserver responseObserver) { + switch (methodId) { + default: + throw new AssertionError(); + } + } + } + + private static abstract class EchoTestServiceBaseDescriptorSupplier + implements io.grpc.protobuf.ProtoFileDescriptorSupplier, io.grpc.protobuf.ProtoServiceDescriptorSupplier { + EchoTestServiceBaseDescriptorSupplier() {} + + @java.lang.Override + public com.google.protobuf.Descriptors.FileDescriptor getFileDescriptor() { + return io.istio.test.Echo.getDescriptor(); + } + + @java.lang.Override + public com.google.protobuf.Descriptors.ServiceDescriptor getServiceDescriptor() { + return getFileDescriptor().findServiceByName("EchoTestService"); + } + } + + private static final class EchoTestServiceFileDescriptorSupplier + extends EchoTestServiceBaseDescriptorSupplier { + EchoTestServiceFileDescriptorSupplier() {} + } + + private static final class EchoTestServiceMethodDescriptorSupplier + extends EchoTestServiceBaseDescriptorSupplier + implements io.grpc.protobuf.ProtoMethodDescriptorSupplier { + private final String methodName; + + EchoTestServiceMethodDescriptorSupplier(String methodName) { + this.methodName = methodName; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.MethodDescriptor getMethodDescriptor() { + return getServiceDescriptor().findMethodByName(methodName); + } + } + + private static volatile io.grpc.ServiceDescriptor serviceDescriptor; + + public static io.grpc.ServiceDescriptor getServiceDescriptor() { + io.grpc.ServiceDescriptor result = serviceDescriptor; + if (result == null) { + synchronized (EchoTestServiceGrpc.class) { + result = serviceDescriptor; + if (result == null) { + serviceDescriptor = result = io.grpc.ServiceDescriptor.newBuilder(SERVICE_NAME) + .setSchemaDescriptor(new EchoTestServiceFileDescriptorSupplier()) + .addMethod(getEchoMethod()) + .addMethod(getForwardEchoMethod()) + .build(); + } + } + } + return result; + } +} diff --git a/istio-interop-testing/src/main/java/io/grpc/testing/istio/EchoTestServer.java b/istio-interop-testing/src/main/java/io/grpc/testing/istio/EchoTestServer.java new file mode 100644 index 0000000000..9118a6a14c --- /dev/null +++ b/istio-interop-testing/src/main/java/io/grpc/testing/istio/EchoTestServer.java @@ -0,0 +1,372 @@ +/* + * Copyright 2022 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.istio; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.ImmutableMap; +import io.grpc.Context; +import io.grpc.Contexts; +import io.grpc.Grpc; +import io.grpc.InsecureChannelCredentials; +import io.grpc.InsecureServerCredentials; +import io.grpc.ManagedChannel; +import io.grpc.ManagedChannelBuilder; +import io.grpc.Metadata; +import io.grpc.Server; +import io.grpc.ServerCall; +import io.grpc.ServerCallHandler; +import io.grpc.ServerInterceptor; +import io.grpc.ServerInterceptors; +import io.grpc.ServerServiceDefinition; +import io.grpc.Status; +import io.grpc.StatusRuntimeException; +import io.grpc.stub.MetadataUtils; +import io.grpc.stub.StreamObserver; +import io.istio.test.Echo.EchoRequest; +import io.istio.test.Echo.EchoResponse; +import io.istio.test.Echo.ForwardEchoRequest; +import io.istio.test.Echo.ForwardEchoResponse; +import io.istio.test.Echo.Header; +import io.istio.test.EchoTestServiceGrpc; +import io.istio.test.EchoTestServiceGrpc.EchoTestServiceBlockingStub; +import io.istio.test.EchoTestServiceGrpc.EchoTestServiceImplBase; +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.time.Duration; +import java.time.Instant; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * This class implements the Istio echo server functionality similar to + * https://github.com/istio/istio/blob/master/pkg/test/echo/server/endpoint/grpc.go . + * Please see Istio framework docs https://github.com/istio/istio/wiki/Istio-Test-Framework . + */ +public final class EchoTestServer { + + private static final Logger logger = Logger.getLogger(EchoTestServer.class.getName()); + + static final Context.Key CLIENT_ADDRESS_CONTEXT_KEY = + Context.key("io.grpc.testing.istio.ClientAddress"); + static final Context.Key AUTHORITY_CONTEXT_KEY = + Context.key("io.grpc.testing.istio.Authority"); + static final Context.Key> REQUEST_HEADERS_CONTEXT_KEY = + Context.key("io.grpc.testing.istio.RequestHeaders"); + + private static final String REQUEST_ID = "x-request-id"; + private static final String STATUS_CODE = "StatusCode"; + private static final String HOST = "Host"; + private static final String HOSTNAME = "Hostname"; + private static final String REQUEST_HEADER = "RequestHeader"; + private static final String IP = "IP"; + public static final String GRPC_SCHEME = "grpc://"; + + @VisibleForTesting List servers; + + /** + * Preprocess args, for two things: + * 1. merge duplicate flags. So "--grpc=8080 --grpc=9090" becomes + * "--grpc=8080,9090". + * 2. replace '-' to '_'. So "--istio-version=123" becomes + * "--istio_version=123" (so exclude the leading "--"). + **/ + @VisibleForTesting + static Map> preprocessArgs(String[] args) { + HashMap> argsMap = new HashMap<>(); + for (String arg : args) { + String[] keyValue = arg.split("=", 2); + + if (keyValue.length == 2) { + String key = keyValue[0]; + String value = keyValue[1]; + + key = key.substring(0, 2) + key.substring(2).replace('-', '_'); + List oldValue = argsMap.get(key); + if (oldValue == null) { + oldValue = new ArrayList<>(); + } + oldValue.add(value); + argsMap.put(key, oldValue); + } + } + return ImmutableMap.>builder().putAll(argsMap).build(); + } + + /** Turn gRPC ports from a string list to an int list. */ + @VisibleForTesting + static List getGrpcPorts(Map> args) { + List grpcPorts = args.get("--grpc"); + List grpcPortsInt = new ArrayList<>(grpcPorts.size()); + + for (String port : grpcPorts) { + grpcPortsInt.add(Integer.parseInt(port)); + } + return grpcPortsInt; + } + + private static String determineHostname() { + try { + return InetAddress.getLocalHost().getHostName(); + } catch (IOException ex) { + logger.log(Level.INFO, "Failed to determine hostname. Will generate one", ex); + } + // let's make an identifier for ourselves. + return "generated-" + new Random().nextInt(); + } + + /** + * The main application allowing this program to be launched from the command line. + */ + public static void main(String[] args) throws Exception { + Map> processedArgs = preprocessArgs(args); + List grpcPorts = getGrpcPorts(processedArgs); + + String hostname = determineHostname(); + EchoTestServer echoTestServer = new EchoTestServer(); + echoTestServer.runServers(grpcPorts, hostname); + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + try { + System.out.println("Shutting down"); + echoTestServer.stopServers(); + } catch (Exception e) { + logger.log(Level.SEVERE, "stopServers", e); + throw e; + } + })); + echoTestServer.blockUntilShutdown(); + } + + void runServers(List grpcPorts, String hostname) throws IOException { + ServerServiceDefinition service = ServerInterceptors.intercept( + new EchoTestServiceImpl(hostname), new EchoTestServerInterceptor()); + servers = new ArrayList<>(grpcPorts.size() + 1); + for (int port : grpcPorts) { + runServer(port, service); + } + } + + void runServer(int port, ServerServiceDefinition service) throws IOException { + logger.log(Level.INFO, "Listening GRPC on " + port); + servers.add(Grpc.newServerBuilderForPort(port, InsecureServerCredentials.create()) + .addService(service) + .build().start()); + } + + void stopServers() { + for (Server server : servers) { + server.shutdownNow(); + } + } + + void blockUntilShutdown() throws InterruptedException { + for (Server server : servers) { + if (!server.awaitTermination(5, TimeUnit.SECONDS)) { + System.err.println("Timed out waiting for server shutdown"); + } + } + } + + private static class EchoTestServerInterceptor implements ServerInterceptor { + + @Override + public ServerCall.Listener interceptCall(ServerCall call, + final Metadata requestHeaders, ServerCallHandler next) { + final String methodName = call.getMethodDescriptor().getBareMethodName(); + + // we need this processing only for Echo + if (!"Echo".equals(methodName)) { + return next.startCall(call, requestHeaders); + } + final SocketAddress peerAddress = call.getAttributes() + .get(Grpc.TRANSPORT_ATTR_REMOTE_ADDR); + + Context ctx = Context.current(); + if (peerAddress instanceof InetSocketAddress) { + InetSocketAddress inetPeerAddress = (InetSocketAddress) peerAddress; + ctx = ctx.withValue(CLIENT_ADDRESS_CONTEXT_KEY, + inetPeerAddress.getAddress().getHostAddress()); + } + ctx = ctx.withValue(AUTHORITY_CONTEXT_KEY, call.getAuthority()); + Map requestHeadersCopy = new HashMap<>(); + for (String key : requestHeaders.keys()) { + if (!key.endsWith("-bin")) { + requestHeadersCopy.put(key, + requestHeaders.get(Metadata.Key.of(key, Metadata.ASCII_STRING_MARSHALLER))); + } + } + ctx = ctx.withValue(REQUEST_HEADERS_CONTEXT_KEY, requestHeadersCopy); + return Contexts.interceptCall( + ctx, + call, + requestHeaders, + next); + } + } + + private static class EchoTestServiceImpl extends EchoTestServiceImplBase { + + private final String hostname; + + EchoTestServiceImpl(String hostname) { + this.hostname = hostname; + } + + @Override + public void echo(EchoRequest request, + io.grpc.stub.StreamObserver responseObserver) { + + EchoMessage echoMessage = new EchoMessage(); + echoMessage.writeKeyValue(HOSTNAME, hostname); + echoMessage.writeKeyValue("Echo", request.getMessage()); + String clientAddress = CLIENT_ADDRESS_CONTEXT_KEY.get(); + if (clientAddress != null) { + echoMessage.writeKeyValue(IP, clientAddress); + } + Map requestHeadersCopy = REQUEST_HEADERS_CONTEXT_KEY.get(); + for (Map.Entry entry : requestHeadersCopy.entrySet()) { + echoMessage.writeKeyValueForRequest(REQUEST_HEADER, entry.getKey(), entry.getValue()); + } + echoMessage.writeKeyValue(STATUS_CODE, "200"); + echoMessage.writeKeyValue(HOST, AUTHORITY_CONTEXT_KEY.get()); + EchoResponse echoResponse = EchoResponse.newBuilder() + .setMessage(echoMessage.toString()) + .build(); + + responseObserver.onNext(echoResponse); + responseObserver.onCompleted(); + } + + @Override + public void forwardEcho(ForwardEchoRequest request, + StreamObserver responseObserver) { + try { + responseObserver.onNext(buildEchoResponse(request)); + responseObserver.onCompleted(); + } catch (InterruptedException e) { + responseObserver.onError(e); + Thread.currentThread().interrupt(); + } catch (Exception e) { + responseObserver.onError(e); + } + } + + private ForwardEchoResponse buildEchoResponse(ForwardEchoRequest request) + throws InterruptedException { + ForwardEchoResponse.Builder forwardEchoResponseBuilder + = ForwardEchoResponse.newBuilder(); + String rawUrl = request.getUrl(); + if (!rawUrl.startsWith(GRPC_SCHEME)) { + throw new StatusRuntimeException( + Status.UNIMPLEMENTED.withDescription("protocol grpc:// required")); + } + rawUrl = rawUrl.substring(GRPC_SCHEME.length()); + + // May need to use xds security if urlScheme is "xds" + ManagedChannelBuilder channelBuilder = Grpc.newChannelBuilder( + rawUrl, InsecureChannelCredentials.create()); + ManagedChannel channel = channelBuilder.build(); + + List
requestHeaders = request.getHeadersList(); + Metadata metadata = new Metadata(); + + for (Header header : requestHeaders) { + metadata.put(Metadata.Key.of(header.getKey(), Metadata.ASCII_STRING_MARSHALLER), + header.getValue()); + } + + int count = request.getCount() == 0 ? 1 : request.getCount(); + Duration durationPerQuery = Duration.ZERO; + if (request.getQps() > 0) { + durationPerQuery = Duration.ofNanos( + Duration.ofSeconds(1).toNanos() / request.getQps()); + } + logger.info("qps=" + request.getQps()); + logger.info("durationPerQuery=" + durationPerQuery); + EchoRequest echoRequest = EchoRequest.newBuilder() + .setMessage(request.getMessage()) + .build(); + Instant start = Instant.now(); + logger.info("starting instant=" + start); + Duration expected = Duration.ZERO; + for (int i = 0; i < count; i++) { + Metadata currentMetadata = new Metadata(); + currentMetadata.merge(metadata); + currentMetadata.put( + Metadata.Key.of(REQUEST_ID, Metadata.ASCII_STRING_MARSHALLER), "" + i); + EchoTestServiceBlockingStub stub + = EchoTestServiceGrpc.newBlockingStub(channel).withInterceptors( + MetadataUtils.newAttachHeadersInterceptor(currentMetadata)) + .withDeadlineAfter(request.getTimeoutMicros(), TimeUnit.MICROSECONDS); + String response = callEcho(stub, echoRequest, i); + forwardEchoResponseBuilder.addOutput(response); + Instant current = Instant.now(); + logger.info("after rpc instant=" + current); + Duration elapsed = Duration.between(start, current); + expected = expected.plus(durationPerQuery); + Duration timeLeft = expected.minus(elapsed); + logger.info("elapsed=" + elapsed + ", expected=" + expected + ", timeLeft=" + timeLeft); + if (!timeLeft.isNegative()) { + logger.info("sleeping for ms =" + timeLeft); + Thread.sleep(timeLeft.toMillis()); + } + } + return forwardEchoResponseBuilder.build(); + } + + private String callEcho(EchoTestServiceBlockingStub stub, + EchoRequest echoRequest, int count) { + try { + EchoResponse echoResponse = stub.echo(echoRequest); + return echoResponse.getMessage(); + } catch (Exception e) { + logger.log(Level.INFO, "RPC failed " + count, e); + } + return ""; + } + } + + private static class EchoMessage { + private final StringBuilder sb = new StringBuilder(); + + void writeKeyValue(String key, String value) { + sb.append(key).append("=").append(value).append("\n"); + } + + void writeKeyValueForRequest(String requestHeader, String key, String value) { + if (value != null) { + writeKeyValue(requestHeader, key + ":" + value); + } + } + + void writeMessage(String message) { + sb.append(message); + } + + @Override + public String toString() { + return sb.toString(); + } + } +} diff --git a/istio-interop-testing/src/test/java/io/grpc/testing/istio/EchoTestServerTest.java b/istio-interop-testing/src/test/java/io/grpc/testing/istio/EchoTestServerTest.java new file mode 100644 index 0000000000..c83addab9d --- /dev/null +++ b/istio-interop-testing/src/test/java/io/grpc/testing/istio/EchoTestServerTest.java @@ -0,0 +1,193 @@ +/* + * Copyright 2022 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.istio; + +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import io.grpc.Grpc; +import io.grpc.InsecureChannelCredentials; +import io.grpc.ManagedChannel; +import io.grpc.ManagedChannelBuilder; +import io.grpc.Metadata; +import io.grpc.stub.MetadataUtils; +import io.istio.test.Echo.EchoRequest; +import io.istio.test.Echo.EchoResponse; +import io.istio.test.Echo.ForwardEchoRequest; +import io.istio.test.Echo.ForwardEchoResponse; +import io.istio.test.Echo.Header; +import io.istio.test.EchoTestServiceGrpc; +import java.io.IOException; +import java.time.Duration; +import java.time.Instant; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** + * Unit tests for {@link EchoTestServer}. + */ +@RunWith(JUnit4.class) +public class EchoTestServerTest { + + @Test + public void preprocessArgsTest() { + String[] splitArgs = TEST_ARGS.split(" "); + Map> processedArgs = EchoTestServer.preprocessArgs(splitArgs); + + assertEquals(processedArgs.keySet(), ImmutableSet.copyOf(EXPECTED_KEY_SET)); + assertEquals(processedArgs.get("--server_first"), ImmutableList.of("16060", "16061")); + assertEquals(processedArgs.get("--bind_ip"), ImmutableList.of("18082")); + assertEquals(processedArgs.get("--bind_localhost"), ImmutableList.of("18084")); + assertEquals(processedArgs.get("--version"), ImmutableList.of("\"v1\"")); + assertEquals(processedArgs.get("--grpc"), ImmutableList.of("\"17070\"", "\"17071\"")); + assertEquals(processedArgs.get("--tls"), ImmutableList.of("18443", "19443")); + assertEquals(processedArgs.get("--cluster"), ImmutableList.of("\"cluster-0\"")); + assertEquals(processedArgs.get("--key"), ImmutableList.of("/cert.key")); + assertEquals(processedArgs.get("--tcp"), ImmutableList.of("\"19090\"", "\"16060\"", + "\"19091\"","\"16061\"","\"19092\"")); + assertEquals(processedArgs.get("--istio_version"), ImmutableList.of("3")); + assertEquals(processedArgs.get("--crt"), ImmutableList.of("/cert.crt")); + assertEquals(processedArgs.get("--metrics"), ImmutableList.of("15014")); + assertEquals( + processedArgs.get("--port"), + ImmutableList.of( + "\"18080\"", + "\"18085\"", + "\"18443\"", + "\"18081\"", + "\"19443\"", + "\"18082\"", + "\"18084\"", + "\"18083\"", + "\"8080\"", + "\"3333\"")); + } + + @Test + public void echoTest() throws IOException, InterruptedException { + EchoTestServer echoTestServer = new EchoTestServer(); + + echoTestServer.runServers(ImmutableList.of(0, 0), "test-host"); + assertEquals(2, echoTestServer.servers.size()); + int port = echoTestServer.servers.get(0).getPort(); + assertNotEquals(0, port); + assertNotEquals(0, echoTestServer.servers.get(1).getPort()); + + ManagedChannelBuilder channelBuilder = + Grpc.newChannelBuilderForAddress("localhost", port, InsecureChannelCredentials.create()); + ManagedChannel channel = channelBuilder.build(); + + Metadata metadata = new Metadata(); + metadata.put(Metadata.Key.of("header1", Metadata.ASCII_STRING_MARSHALLER), "value1"); + metadata.put(Metadata.Key.of("header2", Metadata.ASCII_STRING_MARSHALLER), "value2"); + + EchoTestServiceGrpc.EchoTestServiceBlockingStub stub = + EchoTestServiceGrpc.newBlockingStub(channel) + .withInterceptors(MetadataUtils.newAttachHeadersInterceptor(metadata)); + + EchoRequest echoRequest = EchoRequest.newBuilder() + .setMessage("test-message1") + .build(); + EchoResponse echoResponse = stub.echo(echoRequest); + String echoMessage = echoResponse.getMessage(); + Set lines = ImmutableSet.copyOf(echoMessage.split("\n")); + + assertThat(lines).contains("RequestHeader=header1:value1"); + assertThat(lines).contains("RequestHeader=header2:value2"); + assertThat(lines).contains("Echo=test-message1"); + assertThat(lines).contains("Hostname=test-host"); + assertThat(lines).contains("Host=localhost:" + port); + assertThat(lines).contains("StatusCode=200"); + + echoTestServer.stopServers(); + echoTestServer.blockUntilShutdown(); + } + + static final int COUNT_OF_REQUESTS_TO_FORWARD = 60; + + @Test + public void forwardEchoTest() throws IOException { + EchoTestServer echoTestServer = new EchoTestServer(); + + echoTestServer.runServers(ImmutableList.of(0, 0), "test-host"); + assertEquals(2, echoTestServer.servers.size()); + int port1 = echoTestServer.servers.get(0).getPort(); + int port2 = echoTestServer.servers.get(1).getPort(); + + ManagedChannelBuilder channelBuilder = + Grpc.newChannelBuilderForAddress("localhost", port1, InsecureChannelCredentials.create()); + ManagedChannel channel = channelBuilder.build(); + + ForwardEchoRequest forwardEchoRequest = + ForwardEchoRequest.newBuilder() + .setCount(COUNT_OF_REQUESTS_TO_FORWARD) + .setQps(100) + .setTimeoutMicros(2000_000L) // 2000 millis + .setUrl("grpc://localhost:" + port2) + .addHeaders( + Header.newBuilder().setKey("test-key1").setValue("test-value1").build()) + .addHeaders( + Header.newBuilder().setKey("test-key2").setValue("test-value2").build()) + .setMessage("forward-echo-test-message") + .build(); + + EchoTestServiceGrpc.EchoTestServiceBlockingStub stub = + EchoTestServiceGrpc.newBlockingStub(channel); + + Instant start = Instant.now(); + ForwardEchoResponse forwardEchoResponse = stub.forwardEcho(forwardEchoRequest); + Instant end = Instant.now(); + List outputs = forwardEchoResponse.getOutputList(); + assertEquals(COUNT_OF_REQUESTS_TO_FORWARD, outputs.size()); + for (int i = 0; i < COUNT_OF_REQUESTS_TO_FORWARD; i++) { + validateOutput(outputs.get(i), i); + } + long duration = Duration.between(start, end).toMillis(); + assertThat(duration).isAtLeast(COUNT_OF_REQUESTS_TO_FORWARD * 10L); + } + + private static void validateOutput(String output, int i) { + assertThat(output).contains("RequestHeader=x-request-id:" + i); + assertThat(output).contains("RequestHeader=test-key1:test-value1"); + assertThat(output).contains("RequestHeader=test-key2:test-value2"); + assertThat(output).contains("Hostname=test-host"); + assertThat(output).contains("StatusCode=200"); + assertThat(output).contains("Echo=forward-echo-test-message"); + } + + private static final String[] EXPECTED_KEY_SET = { + "--server_first", + "--bind_ip", "--istio_version", "--bind_localhost", "--version", "--grpc", "--tls", + "--cluster", "--key", "--tcp", "--crt", "--metrics", "--port" + }; + + private static final String TEST_ARGS = + "--metrics=15014 --cluster=\"cluster-0\" --port=\"18080\" --grpc=\"17070\" --port=\"18085\"" + + " --tcp=\"19090\" --port=\"18443\" --tls=18443 --tcp=\"16060\" --server_first=16060" + + " --tcp=\"19091\" --tcp=\"16061\" --server_first=16061 --port=\"18081\"" + + " --grpc=\"17071\" --port=\"19443\" --tls=19443 --port=\"18082\" --bind_ip=18082" + + " --port=\"18084\" --bind_localhost=18084 --tcp=\"19092\" --port=\"18083\"" + + " --port=\"8080\" --port=\"3333\" --version=\"v1\" --istio-version=3 --crt=/cert.crt" + + " --key=/cert.key"; +} diff --git a/istio-interop-testing/third_party/istio/LICENSE b/istio-interop-testing/third_party/istio/LICENSE new file mode 100644 index 0000000000..56e48aa37f --- /dev/null +++ b/istio-interop-testing/third_party/istio/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2016-2020 Istio 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. diff --git a/istio-interop-testing/third_party/istio/import.sh b/istio-interop-testing/third_party/istio/import.sh new file mode 100755 index 0000000000..739c3c20fc --- /dev/null +++ b/istio-interop-testing/third_party/istio/import.sh @@ -0,0 +1,58 @@ +#!/bin/bash +# Copyright 2018 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. + +# Update VERSION then in this directory run ./import.sh + +set -e +BRANCH=master +# import VERSION from the istio repository +VERSION=cbee1999ad8b0f1ec790ec47f9ea33fed887f4a7 +GIT_REPO="https://github.com/istio/istio.git" +GIT_BASE_DIR=istio +SOURCE_PROTO_BASE_DIR=istio/pkg +TARGET_PROTO_BASE_DIR=src/main/proto +# Sorted alphabetically. +FILES=( +test/echo/proto/echo.proto +) + +pushd `git rev-parse --show-toplevel`/istio-interop-testing/third_party/istio + +# clone the istio github repo in a tmp directory +tmpdir="$(mktemp -d)" +trap "rm -rf ${tmpdir}" EXIT + +pushd "${tmpdir}" +git clone -b $BRANCH $GIT_REPO +trap "rm -rf $GIT_BASE_DIR" EXIT +cd "$GIT_BASE_DIR" +git checkout $VERSION +popd + +cp -p "${tmpdir}/${GIT_BASE_DIR}/LICENSE" LICENSE + +rm -rf "${TARGET_PROTO_BASE_DIR}" +mkdir -p "${TARGET_PROTO_BASE_DIR}" +pushd "${TARGET_PROTO_BASE_DIR}" + +# copy proto files to project directory +for file in "${FILES[@]}" +do + mkdir -p "$(dirname "${file}")" + cp -p "${tmpdir}/${SOURCE_PROTO_BASE_DIR}/${file}" "${file}" +done +popd + +popd diff --git a/istio-interop-testing/third_party/istio/src/main/proto/test/echo/proto/echo.proto b/istio-interop-testing/third_party/istio/src/main/proto/test/echo/proto/echo.proto new file mode 100644 index 0000000000..7e931b13b9 --- /dev/null +++ b/istio-interop-testing/third_party/istio/src/main/proto/test/echo/proto/echo.proto @@ -0,0 +1,93 @@ +// Copyright Istio Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +import "google/protobuf/wrappers.proto"; + +// Generate with protoc --go_out=. echo.proto -I /work/common-protos/ -I. +package proto; +option go_package="../proto"; +option java_package = "io.istio.test"; +option java_outer_classname = "Echo"; + +service EchoTestService { + rpc Echo (EchoRequest) returns (EchoResponse); + rpc ForwardEcho (ForwardEchoRequest) returns (ForwardEchoResponse); +} + +message EchoRequest { + string message = 1; +} + +message EchoResponse { + string message = 1; +} + +message Header { + string key = 1; + string value = 2; +} + +message ForwardEchoRequest { + int32 count = 1; + int32 qps = 2; + int64 timeout_micros = 3; + string url = 4; + repeated Header headers = 5; + string message = 6; + // Method for the request. Valid only for HTTP + string method = 9; + // If true, requests will be sent using h2c prior knowledge + bool http2 = 7; + // If true, requests will be sent using http3 + bool http3 = 15; + // If true, requests will not be sent until magic string is received + bool serverFirst = 8; + // If true, 301 redirects will be followed + bool followRedirects = 14; + // If non-empty, make the request with the corresponding cert and key. + string cert = 10; + string key = 11; + // If non-empty, verify the server CA + string caCert = 12; + // If non-empty, make the request with the corresponding cert and key file. + string certFile = 16; + string keyFile = 17; + // If non-empty, verify the server CA with the ca cert file. + string caCertFile = 18; + // Skip verifying peer's certificate. + bool insecureSkipVerify = 19; + // List of ALPNs to present. If not set, this will be automatically be set based on the protocol + Alpn alpn = 13; + // Server name (SNI) to present in TLS connections. If not set, Host will be used for http requests. + string serverName = 20; + // Expected response determines what string to look for in the response to validate TCP requests succeeded. + // If not set, defaults to "StatusCode=200" + google.protobuf.StringValue expectedResponse = 21; + // If set, a new connection will be made to the server for each individual request. If false, an attempt + // will be made to re-use the connection for the life of the forward request. This is automatically + // set for DNS, TCP, TLS, and WebSocket protocols. + bool newConnectionPerRequest = 22; + // If set, each request will force a DNS lookup. Only applies if newConnectionPerRequest is set. + bool forceDNSLookup = 23; +} + +message Alpn { + repeated string value = 1; +} + +message ForwardEchoResponse { + repeated string output = 1; +} diff --git a/settings.gradle b/settings.gradle index 64a1291387..cd687f58a8 100644 --- a/settings.gradle +++ b/settings.gradle @@ -4,6 +4,7 @@ pluginManagement { id "com.android.library" version "4.2.0" id "com.github.johnrengelman.shadow" version "7.1.2" id "com.github.kt3k.coveralls" version "2.12.0" + id "com.google.cloud.tools.jib" version "3.2.1" id "com.google.osdetector" version "1.7.0" id "com.google.protobuf" version "0.8.18" id "digital.wup.android-maven-publish" version "3.6.3" @@ -54,6 +55,7 @@ include ":grpc-bom" include ":grpc-rls" include ":grpc-authz" include ":grpc-gcp-observability" +include ":grpc-istio-interop-testing" project(':grpc-api').projectDir = "$rootDir/api" as File project(':grpc-core').projectDir = "$rootDir/core" as File @@ -81,6 +83,7 @@ project(':grpc-bom').projectDir = "$rootDir/bom" as File project(':grpc-rls').projectDir = "$rootDir/rls" as File project(':grpc-authz').projectDir = "$rootDir/authz" as File project(':grpc-gcp-observability').projectDir = "$rootDir/gcp-observability" as File +project(':grpc-istio-interop-testing').projectDir = "$rootDir/istio-interop-testing" as File if (settings.hasProperty('skipCodegen') && skipCodegen.toBoolean()) { println '*** Skipping the build of codegen and compilation of proto files because skipCodegen=true'