Skip to content

Commit

Permalink
Make channelz work with proto lite (#11685)
Browse files Browse the repository at this point in the history
Allows android apps to expose internal grpc state for debugging.
  • Loading branch information
jdcormie authored Nov 14, 2024
1 parent 921f88a commit b170334
Show file tree
Hide file tree
Showing 2 changed files with 149 additions and 87 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -79,6 +80,8 @@

/**
* A static utility class for turning internal data structures into protos.
*
* <p>Works with both regular and lite protos.
*/
final class ChannelzProtoUtil {
private static final Logger logger = Logger.getLogger(ChannelzProtoUtil.class.getName());
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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();
}

Expand Down Expand Up @@ -380,10 +380,11 @@ private static ChannelTrace toChannelTrace(InternalChannelz.ChannelTrace channel
private static List<ChannelTraceEvent> toChannelTraceEvents(List<Event> events) {
List<ChannelTraceEvent> 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));
}
Expand All @@ -395,14 +396,39 @@ private static List<ChannelTraceEvent> toChannelTraceEvents(List<Event> 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;
}
}

Expand Down Expand Up @@ -468,4 +494,12 @@ private static <T> T getFuture(ListenableFuture<T> 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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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));
Expand All @@ -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));
}
Expand Down Expand Up @@ -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));
Expand Down

0 comments on commit b170334

Please # to comment.