From b1703345f74fd211f9c5199825f2b8973885ea0a Mon Sep 17 00:00:00 2001 From: John Cormie Date: Wed, 13 Nov 2024 16:50:14 -0800 Subject: [PATCH] Make channelz work with proto lite (#11685) Allows android apps to expose internal grpc state for debugging. --- .../protobuf/services/ChannelzProtoUtil.java | 72 ++++++-- .../services/ChannelzProtoUtilTest.java | 164 ++++++++++-------- 2 files changed, 149 insertions(+), 87 deletions(-) diff --git a/services/src/main/java/io/grpc/protobuf/services/ChannelzProtoUtil.java b/services/src/main/java/io/grpc/protobuf/services/ChannelzProtoUtil.java index cf003b2f881..74448a8c5bf 100644 --- a/services/src/main/java/io/grpc/protobuf/services/ChannelzProtoUtil.java +++ b/services/src/main/java/io/grpc/protobuf/services/ChannelzProtoUtil.java @@ -21,6 +21,7 @@ import com.google.protobuf.Any; import com.google.protobuf.ByteString; import com.google.protobuf.Int64Value; +import com.google.protobuf.MessageLite; import com.google.protobuf.util.Durations; import com.google.protobuf.util.Timestamps; import io.grpc.ConnectivityState; @@ -79,6 +80,8 @@ /** * A static utility class for turning internal data structures into protos. + * + *

Works with both regular and lite protos. */ final class ChannelzProtoUtil { private static final Logger logger = Logger.getLogger(ChannelzProtoUtil.class.getName()); @@ -254,22 +257,20 @@ static SocketOption toSocketOptionLinger(int lingerSeconds) { } else { lingerOpt = SocketOptionLinger.getDefaultInstance(); } - return SocketOption - .newBuilder() + return SocketOption.newBuilder() .setName(SO_LINGER) - .setAdditional(Any.pack(lingerOpt)) + .setAdditional(packToAny("SocketOptionLinger", lingerOpt)) .build(); } static SocketOption toSocketOptionTimeout(String name, int timeoutMillis) { Preconditions.checkNotNull(name); - return SocketOption - .newBuilder() + return SocketOption.newBuilder() .setName(name) .setAdditional( - Any.pack( - SocketOptionTimeout - .newBuilder() + packToAny( + "SocketOptionTimeout", + SocketOptionTimeout.newBuilder() .setDuration(Durations.fromMillis(timeoutMillis)) .build())) .build(); @@ -307,10 +308,9 @@ static SocketOption toSocketOptionTcpInfo(InternalChannelz.TcpInfo i) { .setTcpiAdvmss(i.advmss) .setTcpiReordering(i.reordering) .build(); - return SocketOption - .newBuilder() + return SocketOption.newBuilder() .setName(TCP_INFO) - .setAdditional(Any.pack(tcpInfo)) + .setAdditional(packToAny("SocketOptionTcpInfo", tcpInfo)) .build(); } @@ -380,10 +380,11 @@ private static ChannelTrace toChannelTrace(InternalChannelz.ChannelTrace channel private static List toChannelTraceEvents(List events) { List channelTraceEvents = new ArrayList<>(); for (Event event : events) { - ChannelTraceEvent.Builder builder = ChannelTraceEvent.newBuilder() - .setDescription(event.description) - .setSeverity(Severity.valueOf(event.severity.name())) - .setTimestamp(Timestamps.fromNanos(event.timestampNanos)); + ChannelTraceEvent.Builder builder = + ChannelTraceEvent.newBuilder() + .setDescription(event.description) + .setSeverity(toSeverity(event.severity)) + .setTimestamp(Timestamps.fromNanos(event.timestampNanos)); if (event.channelRef != null) { builder.setChannelRef(toChannelRef(event.channelRef)); } @@ -395,14 +396,39 @@ private static List toChannelTraceEvents(List events) return Collections.unmodifiableList(channelTraceEvents); } + static Severity toSeverity(Event.Severity severity) { + if (severity == null) { + return Severity.CT_UNKNOWN; + } + switch (severity) { + case CT_INFO: + return Severity.CT_INFO; + case CT_ERROR: + return Severity.CT_ERROR; + case CT_WARNING: + return Severity.CT_WARNING; + default: + return Severity.CT_UNKNOWN; + } + } + static State toState(ConnectivityState state) { if (state == null) { return State.UNKNOWN; } - try { - return Enum.valueOf(State.class, state.name()); - } catch (IllegalArgumentException e) { - return State.UNKNOWN; + switch (state) { + case IDLE: + return State.IDLE; + case READY: + return State.READY; + case CONNECTING: + return State.CONNECTING; + case SHUTDOWN: + return State.SHUTDOWN; + case TRANSIENT_FAILURE: + return State.TRANSIENT_FAILURE; + default: + return State.UNKNOWN; } } @@ -468,4 +494,12 @@ private static T getFuture(ListenableFuture future) { throw Status.INTERNAL.withCause(e).asRuntimeException(); } } + + // A version of Any.pack() that works with protolite. + private static Any packToAny(String typeName, MessageLite value) { + return Any.newBuilder() + .setTypeUrl("type.googleapis.com/grpc.channelz.v1." + typeName) + .setValue(value.toByteString()) + .build(); + } } diff --git a/services/src/test/java/io/grpc/protobuf/services/ChannelzProtoUtilTest.java b/services/src/test/java/io/grpc/protobuf/services/ChannelzProtoUtilTest.java index 0d2e6063d5e..598a8625e58 100644 --- a/services/src/test/java/io/grpc/protobuf/services/ChannelzProtoUtilTest.java +++ b/services/src/test/java/io/grpc/protobuf/services/ChannelzProtoUtilTest.java @@ -27,7 +27,7 @@ import com.google.protobuf.Any; import com.google.protobuf.ByteString; import com.google.protobuf.Int64Value; -import com.google.protobuf.Message; +import com.google.protobuf.MessageLite; import com.google.protobuf.util.Durations; import com.google.protobuf.util.Timestamps; import io.grpc.ConnectivityState; @@ -154,33 +154,44 @@ public final class ChannelzProtoUtilTest { .setData(serverData) .build(); - private final SocketOption sockOptLingerDisabled = SocketOption - .newBuilder() - .setName("SO_LINGER") - .setAdditional( - Any.pack(SocketOptionLinger.getDefaultInstance())) - .build(); - - private final SocketOption sockOptlinger10s = SocketOption - .newBuilder() - .setName("SO_LINGER") - .setAdditional( - Any.pack(SocketOptionLinger - .newBuilder() - .setActive(true) - .setDuration(Durations.fromSeconds(10)) - .build())) - .build(); - - private final SocketOption sockOptTimeout200ms = SocketOption - .newBuilder() - .setName("SO_TIMEOUT") - .setAdditional( - Any.pack(SocketOptionTimeout - .newBuilder() - .setDuration(Durations.fromMillis(200)) - .build()) - ).build(); + private final SocketOption sockOptLingerDisabled = + SocketOption.newBuilder() + .setName("SO_LINGER") + .setAdditional( + Any.newBuilder() + .setTypeUrl("type.googleapis.com/grpc.channelz.v1.SocketOptionLinger") + .setValue(SocketOptionLinger.getDefaultInstance().toByteString()) + .build()) + .build(); + + private final SocketOption sockOptlinger10s = + SocketOption.newBuilder() + .setName("SO_LINGER") + .setAdditional( + Any.newBuilder() + .setTypeUrl("type.googleapis.com/grpc.channelz.v1.SocketOptionLinger") + .setValue( + SocketOptionLinger.newBuilder() + .setActive(true) + .setDuration(Durations.fromSeconds(10)) + .build() + .toByteString()) + .build()) + .build(); + + private final SocketOption sockOptTimeout200ms = + SocketOption.newBuilder() + .setName("SO_TIMEOUT") + .setAdditional( + Any.newBuilder() + .setTypeUrl("type.googleapis.com/grpc.channelz.v1.SocketOptionTimeout") + .setValue( + SocketOptionTimeout.newBuilder() + .setDuration(Durations.fromMillis(200)) + .build() + .toByteString()) + .build()) + .build(); private final SocketOption sockOptAdditional = SocketOption .newBuilder() @@ -221,43 +232,46 @@ public final class ChannelzProtoUtilTest { .setReordering(728) .build(); - private final SocketOption socketOptionTcpInfo = SocketOption - .newBuilder() - .setName("TCP_INFO") - .setAdditional( - Any.pack( - SocketOptionTcpInfo.newBuilder() - .setTcpiState(70) - .setTcpiCaState(71) - .setTcpiRetransmits(72) - .setTcpiProbes(73) - .setTcpiBackoff(74) - .setTcpiOptions(75) - .setTcpiSndWscale(76) - .setTcpiRcvWscale(77) - .setTcpiRto(78) - .setTcpiAto(79) - .setTcpiSndMss(710) - .setTcpiRcvMss(711) - .setTcpiUnacked(712) - .setTcpiSacked(713) - .setTcpiLost(714) - .setTcpiRetrans(715) - .setTcpiFackets(716) - .setTcpiLastDataSent(717) - .setTcpiLastAckSent(718) - .setTcpiLastDataRecv(719) - .setTcpiLastAckRecv(720) - .setTcpiPmtu(721) - .setTcpiRcvSsthresh(722) - .setTcpiRtt(723) - .setTcpiRttvar(724) - .setTcpiSndSsthresh(725) - .setTcpiSndCwnd(726) - .setTcpiAdvmss(727) - .setTcpiReordering(728) - .build())) - .build(); + private final SocketOption socketOptionTcpInfo = + SocketOption.newBuilder() + .setName("TCP_INFO") + .setAdditional( + Any.newBuilder() + .setTypeUrl("type.googleapis.com/grpc.channelz.v1.SocketOptionTcpInfo") + .setValue( + SocketOptionTcpInfo.newBuilder() + .setTcpiState(70) + .setTcpiCaState(71) + .setTcpiRetransmits(72) + .setTcpiProbes(73) + .setTcpiBackoff(74) + .setTcpiOptions(75) + .setTcpiSndWscale(76) + .setTcpiRcvWscale(77) + .setTcpiRto(78) + .setTcpiAto(79) + .setTcpiSndMss(710) + .setTcpiRcvMss(711) + .setTcpiUnacked(712) + .setTcpiSacked(713) + .setTcpiLost(714) + .setTcpiRetrans(715) + .setTcpiFackets(716) + .setTcpiLastDataSent(717) + .setTcpiLastAckSent(718) + .setTcpiLastDataRecv(719) + .setTcpiLastAckRecv(720) + .setTcpiPmtu(721) + .setTcpiRcvSsthresh(722) + .setTcpiRtt(723) + .setTcpiRttvar(724) + .setTcpiSndSsthresh(725) + .setTcpiSndCwnd(726) + .setTcpiAdvmss(727) + .setTcpiReordering(728) + .build() + .toByteString())) + .build(); private final TestListenSocket listenSocket = new TestListenSocket(); private final SocketRef listenSocketRef = SocketRef @@ -336,6 +350,16 @@ public void toServerRef() { assertEquals(serverRef, ChannelzProtoUtil.toServerRef(server)); } + @Test + public void toSeverity() { + for (Severity severity : Severity.values()) { + assertEquals( + severity.name(), + ChannelzProtoUtil.toSeverity(severity).name()); // OK because test isn't proguarded. + } + assertEquals(ChannelTraceEvent.Severity.CT_UNKNOWN, ChannelzProtoUtil.toSeverity(null)); + } + @Test public void toSocketRef() { assertEquals(socketRef, ChannelzProtoUtil.toSocketRef(socket)); @@ -346,7 +370,7 @@ public void toState() { for (ConnectivityState connectivityState : ConnectivityState.values()) { assertEquals( connectivityState.name(), - ChannelzProtoUtil.toState(connectivityState).getValueDescriptor().getName()); + ChannelzProtoUtil.toState(connectivityState).name()); // OK because test isn't proguarded. } assertEquals(State.UNKNOWN, ChannelzProtoUtil.toState(null)); } @@ -475,8 +499,12 @@ public void socketSecurityTls() throws Exception { @Test public void socketSecurityOther() throws Exception { // what is packed here is not important, just pick some proto message - Message contents = GetChannelRequest.newBuilder().setChannelId(1).build(); - Any packed = Any.pack(contents); + MessageLite contents = GetChannelRequest.newBuilder().setChannelId(1).build(); + Any packed = + Any.newBuilder() + .setTypeUrl("type.googleapis.com/grpc.channelz.v1.GetChannelRequest") + .setValue(contents.toByteString()) + .build(); socket.security = new InternalChannelz.Security( new InternalChannelz.OtherSecurity("other_security", packed));