snapshot of https://github.com/grpc/grpc-go.git (master, 2026-09-15; blobs > 131072B omitted)
Some checks failed
codecov / upload (push) Failing after 13m59s
CodeQL / Analyze (push) Failing after 1s
Testing / vet-proto (push) Failing after 1s
Testing / tests (arm, latest) (push) Waiting to run
Testing / extras (latest) (push) Failing after 1s
Testing / static checks (latest-1) (push) Failing after 1s
Testing / tests (-race, latest) (push) Failing after 1s
Testing / tests (i386, latest) (push) Failing after 1s
Testing / tests (latest) (push) Failing after 1s
Testing / tests (latest-1) (push) Failing after 1s
Lock Threads / lock (push) Failing after 1s
govulncheck / govulncheck (.) (push) Failing after 1s
govulncheck / govulncheck (./cmd/protoc-gen-go-grpc) (push) Failing after 1s
govulncheck / govulncheck (./gcp/observability) (push) Failing after 1s
govulncheck / govulncheck (./security/advancedtls) (push) Failing after 1s
govulncheck / govulncheck (./stats/opencensus) (push) Failing after 1s
Stale bot / stale (push) Failing after 1s

This commit is contained in:
upstream-mirror
2026-09-15 20:40:15 +05:00
commit e241b33f26
1365 changed files with 339570 additions and 0 deletions

36
channelz/channelz.go Normal file
View File

@@ -0,0 +1,36 @@
/*
*
* Copyright 2020 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 channelz exports internals of the channelz implementation as required
// by other gRPC packages.
//
// The implementation of the channelz spec as defined in
// https://github.com/grpc/proposal/blob/master/A14-channelz.md, is provided by
// the `internal/channelz` package.
//
// # Experimental
//
// Notice: All APIs in this package are experimental and may be removed in a
// later release.
package channelz
import "google.golang.org/grpc/internal/channelz"
// Identifier is an opaque identifier which uniquely identifies an entity in the
// channelz database.
type Identifier = channelz.Identifier

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,390 @@
// 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.
// This file defines an interface for exporting monitoring information
// out of gRPC servers. See the full design at
// https://github.com/grpc/proposal/blob/master/A14-channelz.md
//
// The canonical version of this proto can be found at
// https://github.com/grpc/grpc-proto/blob/master/grpc/channelz/v1/channelz.proto
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.6.2
// - protoc v5.27.1
// source: grpc/channelz/v1/channelz.proto
package grpc_channelz_v1
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.64.0 or later.
const _ = grpc.SupportPackageIsVersion9
const (
Channelz_GetTopChannels_FullMethodName = "/grpc.channelz.v1.Channelz/GetTopChannels"
Channelz_GetServers_FullMethodName = "/grpc.channelz.v1.Channelz/GetServers"
Channelz_GetServer_FullMethodName = "/grpc.channelz.v1.Channelz/GetServer"
Channelz_GetServerSockets_FullMethodName = "/grpc.channelz.v1.Channelz/GetServerSockets"
Channelz_GetChannel_FullMethodName = "/grpc.channelz.v1.Channelz/GetChannel"
Channelz_GetSubchannel_FullMethodName = "/grpc.channelz.v1.Channelz/GetSubchannel"
Channelz_GetSocket_FullMethodName = "/grpc.channelz.v1.Channelz/GetSocket"
)
// ChannelzClient is the client API for Channelz service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
//
// Channelz is a service exposed by gRPC servers that provides detailed debug
// information.
type ChannelzClient interface {
// Gets all root channels (i.e. channels the application has directly
// created). This does not include subchannels nor non-top level channels.
GetTopChannels(ctx context.Context, in *GetTopChannelsRequest, opts ...grpc.CallOption) (*GetTopChannelsResponse, error)
// Gets all servers that exist in the process.
GetServers(ctx context.Context, in *GetServersRequest, opts ...grpc.CallOption) (*GetServersResponse, error)
// Returns a single Server, or else a NOT_FOUND code.
GetServer(ctx context.Context, in *GetServerRequest, opts ...grpc.CallOption) (*GetServerResponse, error)
// Gets all server sockets that exist in the process.
GetServerSockets(ctx context.Context, in *GetServerSocketsRequest, opts ...grpc.CallOption) (*GetServerSocketsResponse, error)
// Returns a single Channel, or else a NOT_FOUND code.
GetChannel(ctx context.Context, in *GetChannelRequest, opts ...grpc.CallOption) (*GetChannelResponse, error)
// Returns a single Subchannel, or else a NOT_FOUND code.
GetSubchannel(ctx context.Context, in *GetSubchannelRequest, opts ...grpc.CallOption) (*GetSubchannelResponse, error)
// Returns a single Socket or else a NOT_FOUND code.
GetSocket(ctx context.Context, in *GetSocketRequest, opts ...grpc.CallOption) (*GetSocketResponse, error)
}
type channelzClient struct {
cc grpc.ClientConnInterface
}
func NewChannelzClient(cc grpc.ClientConnInterface) ChannelzClient {
return &channelzClient{cc}
}
func (c *channelzClient) GetTopChannels(ctx context.Context, in *GetTopChannelsRequest, opts ...grpc.CallOption) (*GetTopChannelsResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(GetTopChannelsResponse)
err := c.cc.Invoke(ctx, Channelz_GetTopChannels_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *channelzClient) GetServers(ctx context.Context, in *GetServersRequest, opts ...grpc.CallOption) (*GetServersResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(GetServersResponse)
err := c.cc.Invoke(ctx, Channelz_GetServers_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *channelzClient) GetServer(ctx context.Context, in *GetServerRequest, opts ...grpc.CallOption) (*GetServerResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(GetServerResponse)
err := c.cc.Invoke(ctx, Channelz_GetServer_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *channelzClient) GetServerSockets(ctx context.Context, in *GetServerSocketsRequest, opts ...grpc.CallOption) (*GetServerSocketsResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(GetServerSocketsResponse)
err := c.cc.Invoke(ctx, Channelz_GetServerSockets_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *channelzClient) GetChannel(ctx context.Context, in *GetChannelRequest, opts ...grpc.CallOption) (*GetChannelResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(GetChannelResponse)
err := c.cc.Invoke(ctx, Channelz_GetChannel_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *channelzClient) GetSubchannel(ctx context.Context, in *GetSubchannelRequest, opts ...grpc.CallOption) (*GetSubchannelResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(GetSubchannelResponse)
err := c.cc.Invoke(ctx, Channelz_GetSubchannel_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *channelzClient) GetSocket(ctx context.Context, in *GetSocketRequest, opts ...grpc.CallOption) (*GetSocketResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(GetSocketResponse)
err := c.cc.Invoke(ctx, Channelz_GetSocket_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// ChannelzServer is the server API for Channelz service.
// All implementations should embed UnimplementedChannelzServer
// for forward compatibility.
//
// Channelz is a service exposed by gRPC servers that provides detailed debug
// information.
type ChannelzServer interface {
// Gets all root channels (i.e. channels the application has directly
// created). This does not include subchannels nor non-top level channels.
GetTopChannels(context.Context, *GetTopChannelsRequest) (*GetTopChannelsResponse, error)
// Gets all servers that exist in the process.
GetServers(context.Context, *GetServersRequest) (*GetServersResponse, error)
// Returns a single Server, or else a NOT_FOUND code.
GetServer(context.Context, *GetServerRequest) (*GetServerResponse, error)
// Gets all server sockets that exist in the process.
GetServerSockets(context.Context, *GetServerSocketsRequest) (*GetServerSocketsResponse, error)
// Returns a single Channel, or else a NOT_FOUND code.
GetChannel(context.Context, *GetChannelRequest) (*GetChannelResponse, error)
// Returns a single Subchannel, or else a NOT_FOUND code.
GetSubchannel(context.Context, *GetSubchannelRequest) (*GetSubchannelResponse, error)
// Returns a single Socket or else a NOT_FOUND code.
GetSocket(context.Context, *GetSocketRequest) (*GetSocketResponse, error)
}
// UnimplementedChannelzServer should be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedChannelzServer struct{}
func (UnimplementedChannelzServer) GetTopChannels(context.Context, *GetTopChannelsRequest) (*GetTopChannelsResponse, error) {
return nil, status.Error(codes.Unimplemented, "method GetTopChannels not implemented")
}
func (UnimplementedChannelzServer) GetServers(context.Context, *GetServersRequest) (*GetServersResponse, error) {
return nil, status.Error(codes.Unimplemented, "method GetServers not implemented")
}
func (UnimplementedChannelzServer) GetServer(context.Context, *GetServerRequest) (*GetServerResponse, error) {
return nil, status.Error(codes.Unimplemented, "method GetServer not implemented")
}
func (UnimplementedChannelzServer) GetServerSockets(context.Context, *GetServerSocketsRequest) (*GetServerSocketsResponse, error) {
return nil, status.Error(codes.Unimplemented, "method GetServerSockets not implemented")
}
func (UnimplementedChannelzServer) GetChannel(context.Context, *GetChannelRequest) (*GetChannelResponse, error) {
return nil, status.Error(codes.Unimplemented, "method GetChannel not implemented")
}
func (UnimplementedChannelzServer) GetSubchannel(context.Context, *GetSubchannelRequest) (*GetSubchannelResponse, error) {
return nil, status.Error(codes.Unimplemented, "method GetSubchannel not implemented")
}
func (UnimplementedChannelzServer) GetSocket(context.Context, *GetSocketRequest) (*GetSocketResponse, error) {
return nil, status.Error(codes.Unimplemented, "method GetSocket not implemented")
}
func (UnimplementedChannelzServer) testEmbeddedByValue() {}
// UnsafeChannelzServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to ChannelzServer will
// result in compilation errors.
type UnsafeChannelzServer interface {
mustEmbedUnimplementedChannelzServer()
}
func RegisterChannelzServer(s grpc.ServiceRegistrar, srv ChannelzServer) {
// If the following call panics, it indicates UnimplementedChannelzServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&Channelz_ServiceDesc, srv)
}
func _Channelz_GetTopChannels_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetTopChannelsRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ChannelzServer).GetTopChannels(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Channelz_GetTopChannels_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ChannelzServer).GetTopChannels(ctx, req.(*GetTopChannelsRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Channelz_GetServers_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetServersRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ChannelzServer).GetServers(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Channelz_GetServers_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ChannelzServer).GetServers(ctx, req.(*GetServersRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Channelz_GetServer_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetServerRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ChannelzServer).GetServer(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Channelz_GetServer_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ChannelzServer).GetServer(ctx, req.(*GetServerRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Channelz_GetServerSockets_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetServerSocketsRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ChannelzServer).GetServerSockets(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Channelz_GetServerSockets_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ChannelzServer).GetServerSockets(ctx, req.(*GetServerSocketsRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Channelz_GetChannel_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetChannelRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ChannelzServer).GetChannel(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Channelz_GetChannel_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ChannelzServer).GetChannel(ctx, req.(*GetChannelRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Channelz_GetSubchannel_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetSubchannelRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ChannelzServer).GetSubchannel(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Channelz_GetSubchannel_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ChannelzServer).GetSubchannel(ctx, req.(*GetSubchannelRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Channelz_GetSocket_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetSocketRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ChannelzServer).GetSocket(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Channelz_GetSocket_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ChannelzServer).GetSocket(ctx, req.(*GetSocketRequest))
}
return interceptor(ctx, in, info, handler)
}
// Channelz_ServiceDesc is the grpc.ServiceDesc for Channelz service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var Channelz_ServiceDesc = grpc.ServiceDesc{
ServiceName: "grpc.channelz.v1.Channelz",
HandlerType: (*ChannelzServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "GetTopChannels",
Handler: _Channelz_GetTopChannels_Handler,
},
{
MethodName: "GetServers",
Handler: _Channelz_GetServers_Handler,
},
{
MethodName: "GetServer",
Handler: _Channelz_GetServer_Handler,
},
{
MethodName: "GetServerSockets",
Handler: _Channelz_GetServerSockets_Handler,
},
{
MethodName: "GetChannel",
Handler: _Channelz_GetChannel_Handler,
},
{
MethodName: "GetSubchannel",
Handler: _Channelz_GetSubchannel_Handler,
},
{
MethodName: "GetSocket",
Handler: _Channelz_GetSocket_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "grpc/channelz/v1/channelz.proto",
}

View File

@@ -0,0 +1,136 @@
/*
*
* Copyright 2024 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 protoconv
import (
"time"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/connectivity"
"google.golang.org/grpc/internal/channelz"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/timestamppb"
channelzpb "google.golang.org/grpc/channelz/grpc_channelz_v1"
)
func connectivityStateToProto(s *connectivity.State) *channelzpb.ChannelConnectivityState {
if s == nil {
return &channelzpb.ChannelConnectivityState{State: channelzpb.ChannelConnectivityState_UNKNOWN}
}
switch *s {
case connectivity.Idle:
return &channelzpb.ChannelConnectivityState{State: channelzpb.ChannelConnectivityState_IDLE}
case connectivity.Connecting:
return &channelzpb.ChannelConnectivityState{State: channelzpb.ChannelConnectivityState_CONNECTING}
case connectivity.Ready:
return &channelzpb.ChannelConnectivityState{State: channelzpb.ChannelConnectivityState_READY}
case connectivity.TransientFailure:
return &channelzpb.ChannelConnectivityState{State: channelzpb.ChannelConnectivityState_TRANSIENT_FAILURE}
case connectivity.Shutdown:
return &channelzpb.ChannelConnectivityState{State: channelzpb.ChannelConnectivityState_SHUTDOWN}
default:
return &channelzpb.ChannelConnectivityState{State: channelzpb.ChannelConnectivityState_UNKNOWN}
}
}
func channelTraceToProto(ct *channelz.ChannelTrace) *channelzpb.ChannelTrace {
pbt := &channelzpb.ChannelTrace{}
if ct == nil {
return pbt
}
pbt.NumEventsLogged = ct.EventNum
if ts := timestamppb.New(ct.CreationTime); ts.IsValid() {
pbt.CreationTimestamp = ts
}
events := make([]*channelzpb.ChannelTraceEvent, 0, len(ct.Events))
for _, e := range ct.Events {
cte := &channelzpb.ChannelTraceEvent{
Description: e.Desc,
Severity: channelzpb.ChannelTraceEvent_Severity(e.Severity),
}
if ts := timestamppb.New(e.Timestamp); ts.IsValid() {
cte.Timestamp = ts
}
if e.RefID != 0 {
switch e.RefType {
case channelz.RefChannel:
cte.ChildRef = &channelzpb.ChannelTraceEvent_ChannelRef{ChannelRef: &channelzpb.ChannelRef{ChannelId: e.RefID, Name: e.RefName}}
case channelz.RefSubChannel:
cte.ChildRef = &channelzpb.ChannelTraceEvent_SubchannelRef{SubchannelRef: &channelzpb.SubchannelRef{SubchannelId: e.RefID, Name: e.RefName}}
}
}
events = append(events, cte)
}
pbt.Events = events
return pbt
}
func channelToProto(cm *channelz.Channel) *channelzpb.Channel {
c := &channelzpb.Channel{}
c.Ref = &channelzpb.ChannelRef{ChannelId: cm.ID, Name: cm.RefName}
c.Data = &channelzpb.ChannelData{
State: connectivityStateToProto(cm.ChannelMetrics.State.Load()),
Target: strFromPointer(cm.ChannelMetrics.Target.Load()),
CallsStarted: cm.ChannelMetrics.CallsStarted.Load(),
CallsSucceeded: cm.ChannelMetrics.CallsSucceeded.Load(),
CallsFailed: cm.ChannelMetrics.CallsFailed.Load(),
}
if ts := timestamppb.New(time.Unix(0, cm.ChannelMetrics.LastCallStartedTimestamp.Load())); ts.IsValid() {
c.Data.LastCallStartedTimestamp = ts
}
ncs := cm.NestedChans()
nestedChans := make([]*channelzpb.ChannelRef, 0, len(ncs))
for id, ref := range ncs {
nestedChans = append(nestedChans, &channelzpb.ChannelRef{ChannelId: id, Name: ref})
}
c.ChannelRef = nestedChans
scs := cm.SubChans()
subChans := make([]*channelzpb.SubchannelRef, 0, len(scs))
for id, ref := range scs {
subChans = append(subChans, &channelzpb.SubchannelRef{SubchannelId: id, Name: ref})
}
c.SubchannelRef = subChans
c.Data.Trace = channelTraceToProto(cm.Trace())
return c
}
// GetTopChannels returns the protobuf representation of the channels starting
// at startID (max of len), and returns end=true if no top channels exist with
// higher IDs.
func GetTopChannels(startID int64, len int) (channels []*channelzpb.Channel, end bool) {
chans, end := channelz.GetTopChannels(startID, len)
for _, ch := range chans {
channels = append(channels, channelToProto(ch))
}
return channels, end
}
// GetChannel returns the protobuf representation of the channel with the given
// ID.
func GetChannel(id int64) (*channelzpb.Channel, error) {
ch := channelz.GetChannel(id)
if ch == nil {
return nil, status.Errorf(codes.NotFound, "requested channel %d not found", id)
}
return channelToProto(ch), nil
}

View File

@@ -0,0 +1,73 @@
/*
*
* Copyright 2024 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 protoconv
import (
"time"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/internal/channelz"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/timestamppb"
channelzpb "google.golang.org/grpc/channelz/grpc_channelz_v1"
)
func serverToProto(sm *channelz.Server) *channelzpb.Server {
s := &channelzpb.Server{}
s.Ref = &channelzpb.ServerRef{ServerId: sm.ID, Name: sm.RefName}
s.Data = &channelzpb.ServerData{
CallsStarted: sm.ServerMetrics.CallsStarted.Load(),
CallsSucceeded: sm.ServerMetrics.CallsSucceeded.Load(),
CallsFailed: sm.ServerMetrics.CallsFailed.Load(),
}
if ts := timestamppb.New(time.Unix(0, sm.ServerMetrics.LastCallStartedTimestamp.Load())); ts.IsValid() {
s.Data.LastCallStartedTimestamp = ts
}
lss := sm.ListenSockets()
sockets := make([]*channelzpb.SocketRef, 0, len(lss))
for id, ref := range lss {
sockets = append(sockets, &channelzpb.SocketRef{SocketId: id, Name: ref})
}
s.ListenSocket = sockets
return s
}
// GetServers returns the protobuf representation of the servers starting at
// startID (max of len), and returns end=true if no servers exist with higher
// IDs.
func GetServers(startID int64, len int) (servers []*channelzpb.Server, end bool) {
srvs, end := channelz.GetServers(startID, len)
for _, srv := range srvs {
servers = append(servers, serverToProto(srv))
}
return servers, end
}
// GetServer returns the protobuf representation of the server with the given
// ID.
func GetServer(id int64) (*channelzpb.Server, error) {
srv := channelz.GetServer(id)
if srv == nil {
return nil, status.Errorf(codes.NotFound, "requested server %d not found", id)
}
return serverToProto(srv), nil
}

View File

@@ -0,0 +1,136 @@
/*
*
* Copyright 2024 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 protoconv
import (
"net"
"time"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/internal/channelz"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/anypb"
"google.golang.org/protobuf/types/known/timestamppb"
"google.golang.org/protobuf/types/known/wrapperspb"
channelzpb "google.golang.org/grpc/channelz/grpc_channelz_v1"
)
func securityToProto(se credentials.ChannelzSecurityValue) *channelzpb.Security {
switch v := se.(type) {
case *credentials.TLSChannelzSecurityValue:
return &channelzpb.Security{Model: &channelzpb.Security_Tls_{Tls: &channelzpb.Security_Tls{
CipherSuite: &channelzpb.Security_Tls_StandardName{StandardName: v.StandardName},
LocalCertificate: v.LocalCertificate,
RemoteCertificate: v.RemoteCertificate,
}}}
case *credentials.OtherChannelzSecurityValue:
otherSecurity := &channelzpb.Security_OtherSecurity{
Name: v.Name,
}
if anyval, err := anypb.New(v.Value); err == nil {
otherSecurity.Value = anyval
}
return &channelzpb.Security{Model: &channelzpb.Security_Other{Other: otherSecurity}}
}
return nil
}
func addrToProto(a net.Addr) *channelzpb.Address {
if a == nil {
return nil
}
switch a.Network() {
case "udp":
// TODO: Address_OtherAddress{}. Need proto def for Value.
case "ip":
// Note zone info is discarded through the conversion.
return &channelzpb.Address{Address: &channelzpb.Address_TcpipAddress{TcpipAddress: &channelzpb.Address_TcpIpAddress{IpAddress: a.(*net.IPAddr).IP}}}
case "ip+net":
// Note mask info is discarded through the conversion.
return &channelzpb.Address{Address: &channelzpb.Address_TcpipAddress{TcpipAddress: &channelzpb.Address_TcpIpAddress{IpAddress: a.(*net.IPNet).IP}}}
case "tcp":
// Note zone info is discarded through the conversion.
return &channelzpb.Address{Address: &channelzpb.Address_TcpipAddress{TcpipAddress: &channelzpb.Address_TcpIpAddress{IpAddress: a.(*net.TCPAddr).IP, Port: int32(a.(*net.TCPAddr).Port)}}}
case "unix", "unixgram", "unixpacket":
return &channelzpb.Address{Address: &channelzpb.Address_UdsAddress_{UdsAddress: &channelzpb.Address_UdsAddress{Filename: a.String()}}}
default:
}
return &channelzpb.Address{}
}
func socketToProto(skt *channelz.Socket) *channelzpb.Socket {
s := &channelzpb.Socket{}
s.Ref = &channelzpb.SocketRef{SocketId: skt.ID, Name: skt.RefName}
s.Data = &channelzpb.SocketData{
StreamsStarted: skt.SocketMetrics.StreamsStarted.Load(),
StreamsSucceeded: skt.SocketMetrics.StreamsSucceeded.Load(),
StreamsFailed: skt.SocketMetrics.StreamsFailed.Load(),
MessagesSent: skt.SocketMetrics.MessagesSent.Load(),
MessagesReceived: skt.SocketMetrics.MessagesReceived.Load(),
KeepAlivesSent: skt.SocketMetrics.KeepAlivesSent.Load(),
}
if ts := timestamppb.New(time.Unix(0, skt.SocketMetrics.LastLocalStreamCreatedTimestamp.Load())); ts.IsValid() {
s.Data.LastLocalStreamCreatedTimestamp = ts
}
if ts := timestamppb.New(time.Unix(0, skt.SocketMetrics.LastRemoteStreamCreatedTimestamp.Load())); ts.IsValid() {
s.Data.LastRemoteStreamCreatedTimestamp = ts
}
if ts := timestamppb.New(time.Unix(0, skt.SocketMetrics.LastMessageSentTimestamp.Load())); ts.IsValid() {
s.Data.LastMessageSentTimestamp = ts
}
if ts := timestamppb.New(time.Unix(0, skt.SocketMetrics.LastMessageReceivedTimestamp.Load())); ts.IsValid() {
s.Data.LastMessageReceivedTimestamp = ts
}
if skt.EphemeralMetrics != nil {
e := skt.EphemeralMetrics()
s.Data.LocalFlowControlWindow = wrapperspb.Int64(e.LocalFlowControlWindow)
s.Data.RemoteFlowControlWindow = wrapperspb.Int64(e.RemoteFlowControlWindow)
}
s.Data.Option = sockoptToProto(skt.SocketOptions)
s.Security = securityToProto(skt.Security)
s.Local = addrToProto(skt.LocalAddr)
s.Remote = addrToProto(skt.RemoteAddr)
s.RemoteName = skt.RemoteName
return s
}
// GetServerSockets returns the protobuf representation of the server (listen)
// sockets starting at startID (max of len), and returns end=true if no server
// sockets exist with higher IDs.
func GetServerSockets(serverID, startID int64, len int) (sockets []*channelzpb.SocketRef, end bool) {
skts, end := channelz.GetServerSockets(serverID, startID, len)
for _, m := range skts {
sockets = append(sockets, &channelzpb.SocketRef{SocketId: m.ID, Name: m.RefName})
}
return sockets, end
}
// GetSocket returns the protobuf representation of the socket with the given
// ID.
func GetSocket(id int64) (*channelzpb.Socket, error) {
skt := channelz.GetSocket(id)
if skt == nil {
return nil, status.Errorf(codes.NotFound, "requested socket %d not found", id)
}
return socketToProto(skt), nil
}

View File

@@ -0,0 +1,125 @@
/*
*
* Copyright 2018 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 protoconv
import (
"time"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/internal/channelz"
channelzpb "google.golang.org/grpc/channelz/grpc_channelz_v1"
"google.golang.org/protobuf/types/known/anypb"
"google.golang.org/protobuf/types/known/durationpb"
)
var logger = grpclog.Component("channelz")
func convertToPbDuration(sec int64, usec int64) *durationpb.Duration {
return durationpb.New(time.Duration(sec*1e9 + usec*1e3))
}
func sockoptToProto(skopts *channelz.SocketOptionData) []*channelzpb.SocketOption {
if skopts == nil {
return nil
}
var opts []*channelzpb.SocketOption
if skopts.Linger != nil {
additional, err := anypb.New(&channelzpb.SocketOptionLinger{
Active: skopts.Linger.Onoff != 0,
Duration: convertToPbDuration(int64(skopts.Linger.Linger), 0),
})
if err == nil {
opts = append(opts, &channelzpb.SocketOption{
Name: "SO_LINGER",
Additional: additional,
})
} else {
logger.Warningf("Failed to marshal socket options linger %+v: %v", skopts.Linger, err)
}
}
if skopts.RecvTimeout != nil {
additional, err := anypb.New(&channelzpb.SocketOptionTimeout{
Duration: convertToPbDuration(int64(skopts.RecvTimeout.Sec), int64(skopts.RecvTimeout.Usec)),
})
if err == nil {
opts = append(opts, &channelzpb.SocketOption{
Name: "SO_RCVTIMEO",
Additional: additional,
})
} else {
logger.Warningf("Failed to marshal socket options receive timeout %+v: %v", skopts.RecvTimeout, err)
}
}
if skopts.SendTimeout != nil {
additional, err := anypb.New(&channelzpb.SocketOptionTimeout{
Duration: convertToPbDuration(int64(skopts.SendTimeout.Sec), int64(skopts.SendTimeout.Usec)),
})
if err == nil {
opts = append(opts, &channelzpb.SocketOption{
Name: "SO_SNDTIMEO",
Additional: additional,
})
} else {
logger.Warningf("Failed to marshal socket options send timeout %+v: %v", skopts.SendTimeout, err)
}
}
if skopts.TCPInfo != nil {
additional, err := anypb.New(&channelzpb.SocketOptionTcpInfo{
TcpiState: uint32(skopts.TCPInfo.State),
TcpiCaState: uint32(skopts.TCPInfo.Ca_state),
TcpiRetransmits: uint32(skopts.TCPInfo.Retransmits),
TcpiProbes: uint32(skopts.TCPInfo.Probes),
TcpiBackoff: uint32(skopts.TCPInfo.Backoff),
TcpiOptions: uint32(skopts.TCPInfo.Options),
// https://golang.org/pkg/syscall/#TCPInfo
// TCPInfo struct does not contain info about TcpiSndWscale and TcpiRcvWscale.
TcpiRto: skopts.TCPInfo.Rto,
TcpiAto: skopts.TCPInfo.Ato,
TcpiSndMss: skopts.TCPInfo.Snd_mss,
TcpiRcvMss: skopts.TCPInfo.Rcv_mss,
TcpiUnacked: skopts.TCPInfo.Unacked,
TcpiSacked: skopts.TCPInfo.Sacked,
TcpiLost: skopts.TCPInfo.Lost,
TcpiRetrans: skopts.TCPInfo.Retrans,
TcpiFackets: skopts.TCPInfo.Fackets,
TcpiLastDataSent: skopts.TCPInfo.Last_data_sent,
TcpiLastAckSent: skopts.TCPInfo.Last_ack_sent,
TcpiLastDataRecv: skopts.TCPInfo.Last_data_recv,
TcpiLastAckRecv: skopts.TCPInfo.Last_ack_recv,
TcpiPmtu: skopts.TCPInfo.Pmtu,
TcpiRcvSsthresh: skopts.TCPInfo.Rcv_ssthresh,
TcpiRtt: skopts.TCPInfo.Rtt,
TcpiRttvar: skopts.TCPInfo.Rttvar,
TcpiSndSsthresh: skopts.TCPInfo.Snd_ssthresh,
TcpiSndCwnd: skopts.TCPInfo.Snd_cwnd,
TcpiAdvmss: skopts.TCPInfo.Advmss,
TcpiReordering: skopts.TCPInfo.Reordering,
})
if err == nil {
opts = append(opts, &channelzpb.SocketOption{
Name: "TCP_INFO",
Additional: additional,
})
} else {
logger.Warningf("Failed to marshal socket options TCP info %+v: %v", skopts.TCPInfo, err)
}
}
return opts
}

View File

@@ -0,0 +1,31 @@
//go:build !linux
// +build !linux
/*
*
* Copyright 2018 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 protoconv
import (
channelzpb "google.golang.org/grpc/channelz/grpc_channelz_v1"
"google.golang.org/grpc/internal/channelz"
)
func sockoptToProto(_ *channelz.SocketOptionData) []*channelzpb.SocketOption {
return nil
}

View File

@@ -0,0 +1,65 @@
/*
*
* Copyright 2024 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 protoconv
import (
"time"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/internal/channelz"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/timestamppb"
channelzpb "google.golang.org/grpc/channelz/grpc_channelz_v1"
)
func subChannelToProto(cm *channelz.SubChannel) *channelzpb.Subchannel {
sc := &channelzpb.Subchannel{}
sc.Ref = &channelzpb.SubchannelRef{SubchannelId: cm.ID, Name: cm.RefName}
sc.Data = &channelzpb.ChannelData{
State: connectivityStateToProto(cm.ChannelMetrics.State.Load()),
Target: strFromPointer(cm.ChannelMetrics.Target.Load()),
CallsStarted: cm.ChannelMetrics.CallsStarted.Load(),
CallsSucceeded: cm.ChannelMetrics.CallsSucceeded.Load(),
CallsFailed: cm.ChannelMetrics.CallsFailed.Load(),
}
if ts := timestamppb.New(time.Unix(0, cm.ChannelMetrics.LastCallStartedTimestamp.Load())); ts.IsValid() {
sc.Data.LastCallStartedTimestamp = ts
}
skts := cm.Sockets()
sockets := make([]*channelzpb.SocketRef, 0, len(skts))
for id, ref := range skts {
sockets = append(sockets, &channelzpb.SocketRef{SocketId: id, Name: ref})
}
sc.SocketRef = sockets
sc.Data.Trace = channelTraceToProto(cm.Trace())
return sc
}
// GetSubChannel returns the protobuf representation of the subchannel with the
// given ID.
func GetSubChannel(id int64) (*channelzpb.Subchannel, error) {
subChan := channelz.GetSubChannel(id)
if subChan == nil {
return nil, status.Errorf(codes.NotFound, "requested sub channel %d not found", id)
}
return subChannelToProto(subChan), nil
}

View File

@@ -0,0 +1,28 @@
/*
*
* Copyright 2024 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 protoconv supports converting between the internal channelz
// implementation and the protobuf representation of all the entities.
package protoconv
func strFromPointer(s *string) string {
if s == nil {
return ""
}
return *s
}

102
channelz/service/service.go Normal file
View File

@@ -0,0 +1,102 @@
/*
*
* Copyright 2018 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 service provides an implementation for channelz service server.
package service
import (
"context"
channelzgrpc "google.golang.org/grpc/channelz/grpc_channelz_v1"
channelzpb "google.golang.org/grpc/channelz/grpc_channelz_v1"
"google.golang.org/grpc"
"google.golang.org/grpc/channelz/internal/protoconv"
"google.golang.org/grpc/internal/channelz"
)
func init() {
channelz.TurnOn()
}
// RegisterChannelzServiceToServer registers the channelz service to the given server.
//
// Note: it is preferred to use the admin API
// (https://pkg.go.dev/google.golang.org/grpc/admin#Register) instead to
// register Channelz and other administrative services.
func RegisterChannelzServiceToServer(s grpc.ServiceRegistrar) {
channelzgrpc.RegisterChannelzServer(s, newCZServer())
}
func newCZServer() channelzgrpc.ChannelzServer {
return &serverImpl{}
}
type serverImpl struct {
channelzgrpc.UnimplementedChannelzServer
}
func (s *serverImpl) GetChannel(_ context.Context, req *channelzpb.GetChannelRequest) (*channelzpb.GetChannelResponse, error) {
ch, err := protoconv.GetChannel(req.GetChannelId())
if err != nil {
return nil, err
}
return &channelzpb.GetChannelResponse{Channel: ch}, nil
}
func (s *serverImpl) GetTopChannels(_ context.Context, req *channelzpb.GetTopChannelsRequest) (*channelzpb.GetTopChannelsResponse, error) {
resp := &channelzpb.GetTopChannelsResponse{}
resp.Channel, resp.End = protoconv.GetTopChannels(req.GetStartChannelId(), int(req.GetMaxResults()))
return resp, nil
}
func (s *serverImpl) GetServer(_ context.Context, req *channelzpb.GetServerRequest) (*channelzpb.GetServerResponse, error) {
srv, err := protoconv.GetServer(req.GetServerId())
if err != nil {
return nil, err
}
return &channelzpb.GetServerResponse{Server: srv}, nil
}
func (s *serverImpl) GetServers(_ context.Context, req *channelzpb.GetServersRequest) (*channelzpb.GetServersResponse, error) {
resp := &channelzpb.GetServersResponse{}
resp.Server, resp.End = protoconv.GetServers(req.GetStartServerId(), int(req.GetMaxResults()))
return resp, nil
}
func (s *serverImpl) GetSubchannel(_ context.Context, req *channelzpb.GetSubchannelRequest) (*channelzpb.GetSubchannelResponse, error) {
subChan, err := protoconv.GetSubChannel(req.GetSubchannelId())
if err != nil {
return nil, err
}
return &channelzpb.GetSubchannelResponse{Subchannel: subChan}, nil
}
func (s *serverImpl) GetServerSockets(_ context.Context, req *channelzpb.GetServerSocketsRequest) (*channelzpb.GetServerSocketsResponse, error) {
resp := &channelzpb.GetServerSocketsResponse{}
resp.SocketRef, resp.End = protoconv.GetServerSockets(req.GetServerId(), req.GetStartSocketId(), int(req.GetMaxResults()))
return resp, nil
}
func (s *serverImpl) GetSocket(_ context.Context, req *channelzpb.GetSocketRequest) (*channelzpb.GetSocketResponse, error) {
socket, err := protoconv.GetSocket(req.GetSocketId())
if err != nil {
return nil, err
}
return &channelzpb.GetSocketResponse{Socket: socket}, nil
}

View File

@@ -0,0 +1,98 @@
//go:build linux && (386 || amd64)
// +build linux
// +build 386 amd64
/*
*
* Copyright 2018 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.
*
*/
// SocketOptions is only supported on linux system. The functions defined in
// this file are to parse the socket option field and the test is specifically
// to verify the behavior of socket option parsing.
package service
import (
"context"
"testing"
"time"
"github.com/google/go-cmp/cmp"
"golang.org/x/sys/unix"
"google.golang.org/grpc/internal/channelz"
"google.golang.org/grpc/internal/testutils"
"google.golang.org/protobuf/testing/protocmp"
"google.golang.org/protobuf/types/known/durationpb"
channelzpb "google.golang.org/grpc/channelz/grpc_channelz_v1"
)
func (s) TestGetSocketOptions(t *testing.T) {
ss := &channelz.Socket{
SocketOptions: &channelz.SocketOptionData{
Linger: &unix.Linger{Onoff: 1, Linger: 2},
RecvTimeout: &unix.Timeval{Sec: 10, Usec: 1},
SendTimeout: &unix.Timeval{},
TCPInfo: &unix.TCPInfo{State: 1},
},
}
svr := newCZServer()
czServer := channelz.RegisterServer("test svr")
defer channelz.RemoveEntry(czServer.ID)
id := channelz.RegisterSocket(&channelz.Socket{SocketType: channelz.SocketTypeNormal, RefName: "0", Parent: czServer, SocketOptions: ss.SocketOptions})
defer channelz.RemoveEntry(id.ID)
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel()
resp, _ := svr.GetSocket(ctx, &channelzpb.GetSocketRequest{SocketId: id.ID})
{
got, want := resp.GetSocket().GetRef(), &channelzpb.SocketRef{SocketId: id.ID, Name: "0"}
if diff := cmp.Diff(got, want, protocmp.Transform()); diff != "" {
t.Fatal("resp.GetSocket() ref (-got +want): ", diff)
}
}
{
got := resp.GetSocket().GetData().GetOption()
want := []*channelzpb.SocketOption{{
Name: "SO_LINGER",
Additional: testutils.MarshalAny(
t,
&channelzpb.SocketOptionLinger{Active: true, Duration: durationpb.New(2 * time.Second)},
),
}, {
Name: "SO_RCVTIMEO",
Additional: testutils.MarshalAny(
t,
&channelzpb.SocketOptionTimeout{Duration: durationpb.New(10*time.Second + time.Microsecond)},
),
}, {
Name: "SO_SNDTIMEO",
Additional: testutils.MarshalAny(
t,
&channelzpb.SocketOptionTimeout{Duration: durationpb.New(0)},
),
}, {
Name: "TCP_INFO",
Additional: testutils.MarshalAny(
t,
&channelzpb.SocketOptionTcpInfo{TcpiState: 1},
),
}}
if diff := cmp.Diff(got, want, protocmp.Transform()); diff != "" {
t.Fatal("resp.GetSocket() options (-got +want): ", diff)
}
}
}

View File

@@ -0,0 +1,803 @@
/*
*
* Copyright 2018 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 service
import (
"context"
"fmt"
"net"
"net/netip"
"strconv"
"strings"
"testing"
"time"
"github.com/google/go-cmp/cmp"
"google.golang.org/grpc/connectivity"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/internal/channelz"
"google.golang.org/grpc/internal/grpctest"
"google.golang.org/protobuf/encoding/prototext"
"google.golang.org/protobuf/reflect/protodesc"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/testing/protocmp"
"google.golang.org/protobuf/types/descriptorpb"
"google.golang.org/protobuf/types/dynamicpb"
"google.golang.org/protobuf/types/known/anypb"
"google.golang.org/protobuf/types/known/timestamppb"
channelzpb "google.golang.org/grpc/channelz/grpc_channelz_v1"
)
func init() {
channelz.TurnOn()
}
type s struct {
grpctest.Tester
}
func Test(t *testing.T) {
grpctest.RunSubTests(t, s{})
}
const defaultTestTimeout = 10 * time.Second
func channelProtoToStruct(c *channelzpb.Channel) (*channelz.ChannelMetrics, error) {
cm := &channelz.ChannelMetrics{}
pdata := c.GetData()
var s connectivity.State
switch pdata.GetState().GetState() {
case channelzpb.ChannelConnectivityState_UNKNOWN:
// TODO: what should we set here?
case channelzpb.ChannelConnectivityState_IDLE:
s = connectivity.Idle
case channelzpb.ChannelConnectivityState_CONNECTING:
s = connectivity.Connecting
case channelzpb.ChannelConnectivityState_READY:
s = connectivity.Ready
case channelzpb.ChannelConnectivityState_TRANSIENT_FAILURE:
s = connectivity.TransientFailure
case channelzpb.ChannelConnectivityState_SHUTDOWN:
s = connectivity.Shutdown
}
cm.State.Store(&s)
tgt := pdata.GetTarget()
cm.Target.Store(&tgt)
cm.CallsStarted.Store(pdata.CallsStarted)
cm.CallsSucceeded.Store(pdata.CallsSucceeded)
cm.CallsFailed.Store(pdata.CallsFailed)
if err := pdata.GetLastCallStartedTimestamp().CheckValid(); err != nil {
return nil, err
}
cm.LastCallStartedTimestamp.Store(int64(pdata.GetLastCallStartedTimestamp().AsTime().UnixNano()))
return cm, nil
}
func convertSocketRefSliceToMap(sktRefs []*channelzpb.SocketRef) map[int64]string {
m := make(map[int64]string)
for _, sr := range sktRefs {
m[sr.SocketId] = sr.Name
}
return m
}
func (s) TestGetTopChannels(t *testing.T) {
tcs := []*channelz.ChannelMetrics{
channelz.NewChannelMetricForTesting(
connectivity.Connecting,
"test.channelz:1234",
6,
2,
3,
time.Now().UTC().UnixNano(),
),
channelz.NewChannelMetricForTesting(
connectivity.Connecting,
"test.channelz:1234",
1,
2,
3,
time.Now().UTC().UnixNano(),
),
channelz.NewChannelMetricForTesting(
connectivity.Shutdown,
"test.channelz:8888",
0,
0,
0,
0,
),
}
for _, c := range tcs {
cz := channelz.RegisterChannel(nil, "test channel")
cz.ChannelMetrics.CopyFrom(c)
defer channelz.RemoveEntry(cz.ID)
}
s := newCZServer()
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel()
resp, _ := s.GetTopChannels(ctx, &channelzpb.GetTopChannelsRequest{StartChannelId: 0})
if !resp.GetEnd() {
t.Fatalf("resp.GetEnd() want true, got %v", resp.GetEnd())
}
for i, c := range resp.GetChannel() {
channel, err := channelProtoToStruct(c)
if err != nil {
t.Fatal(err)
}
if diff := cmp.Diff(tcs[i], channel, protocmp.Transform()); diff != "" {
t.Fatalf("unexpected channel, diff (-want +got):\n%s", diff)
}
}
for i := 0; i < 50; i++ {
cz := channelz.RegisterChannel(nil, "")
defer channelz.RemoveEntry(cz.ID)
}
resp, _ = s.GetTopChannels(ctx, &channelzpb.GetTopChannelsRequest{StartChannelId: 0})
if resp.GetEnd() {
t.Fatalf("resp.GetEnd() want false, got %v", resp.GetEnd())
}
}
func (s) TestGetServers(t *testing.T) {
ss := []*channelz.ServerMetrics{
channelz.NewServerMetricsForTesting(
6,
2,
3,
time.Now().UnixNano(),
),
channelz.NewServerMetricsForTesting(
1,
2,
3,
time.Now().UnixNano(),
),
channelz.NewServerMetricsForTesting(
1,
0,
0,
time.Now().UnixNano(),
),
}
firstID := int64(0)
for i, s := range ss {
svr := channelz.RegisterServer("")
if i == 0 {
firstID = svr.ID
}
svr.ServerMetrics.CopyFrom(s)
defer channelz.RemoveEntry(svr.ID)
}
svr := newCZServer()
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel()
resp, _ := svr.GetServers(ctx, &channelzpb.GetServersRequest{StartServerId: 0})
if !resp.GetEnd() {
t.Fatalf("resp.GetEnd() want true, got %v", resp.GetEnd())
}
serversWant := []*channelzpb.Server{
{
Ref: &channelzpb.ServerRef{ServerId: firstID, Name: ""},
Data: &channelzpb.ServerData{
CallsStarted: 6,
CallsSucceeded: 2,
CallsFailed: 3,
LastCallStartedTimestamp: timestamppb.New(time.Unix(0, ss[0].LastCallStartedTimestamp.Load())),
},
},
{
Ref: &channelzpb.ServerRef{ServerId: firstID + 1, Name: ""},
Data: &channelzpb.ServerData{
CallsStarted: 1,
CallsSucceeded: 2,
CallsFailed: 3,
LastCallStartedTimestamp: timestamppb.New(time.Unix(0, ss[1].LastCallStartedTimestamp.Load())),
},
},
{
Ref: &channelzpb.ServerRef{ServerId: firstID + 2, Name: ""},
Data: &channelzpb.ServerData{
CallsStarted: 1,
CallsSucceeded: 0,
CallsFailed: 0,
LastCallStartedTimestamp: timestamppb.New(time.Unix(0, ss[2].LastCallStartedTimestamp.Load())),
},
},
}
if diff := cmp.Diff(serversWant, resp.GetServer(), protocmp.Transform()); diff != "" {
t.Fatalf("unexpected server, diff (-want +got):\n%s", diff)
}
for i := 0; i < 50; i++ {
id := channelz.RegisterServer("").ID
defer channelz.RemoveEntry(id)
}
resp, _ = svr.GetServers(ctx, &channelzpb.GetServersRequest{StartServerId: 0})
if resp.GetEnd() {
t.Fatalf("resp.GetEnd() want false, got %v", resp.GetEnd())
}
}
func (s) TestGetServerSockets(t *testing.T) {
svrID := channelz.RegisterServer("")
defer channelz.RemoveEntry(svrID.ID)
refNames := []string{"listen socket 1", "normal socket 1", "normal socket 2"}
ids := make([]int64, 3)
ids[0] = channelz.RegisterSocket(&channelz.Socket{SocketType: channelz.SocketTypeListen, Parent: svrID, RefName: refNames[0]}).ID
ids[1] = channelz.RegisterSocket(&channelz.Socket{SocketType: channelz.SocketTypeNormal, Parent: svrID, RefName: refNames[1]}).ID
ids[2] = channelz.RegisterSocket(&channelz.Socket{SocketType: channelz.SocketTypeNormal, Parent: svrID, RefName: refNames[2]}).ID
for _, id := range ids {
defer channelz.RemoveEntry(id)
}
svr := newCZServer()
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel()
resp, _ := svr.GetServerSockets(ctx, &channelzpb.GetServerSocketsRequest{ServerId: svrID.ID, StartSocketId: 0})
if !resp.GetEnd() {
t.Fatalf("resp.GetEnd() want: true, got: %v", resp.GetEnd())
}
// GetServerSockets only return normal sockets.
want := map[int64]string{
ids[1]: refNames[1],
ids[2]: refNames[2],
}
if got := convertSocketRefSliceToMap(resp.GetSocketRef()); !cmp.Equal(got, want) {
t.Fatalf("GetServerSockets want: %#v, got: %#v (resp=%v)", want, got, prototext.Format(resp))
}
for i := 0; i < 50; i++ {
id := channelz.RegisterSocket(&channelz.Socket{SocketType: channelz.SocketTypeNormal, Parent: svrID})
defer channelz.RemoveEntry(id.ID)
}
resp, _ = svr.GetServerSockets(ctx, &channelzpb.GetServerSocketsRequest{ServerId: svrID.ID, StartSocketId: 0})
if resp.GetEnd() {
t.Fatalf("resp.GetEnd() want false, got %v", resp.GetEnd())
}
}
// This test makes a GetServerSockets with a non-zero start ID, and expect only
// sockets with ID >= the given start ID.
func (s) TestGetServerSocketsNonZeroStartID(t *testing.T) {
svrID := channelz.RegisterServer("test server")
defer channelz.RemoveEntry(svrID.ID)
refNames := []string{"listen socket 1", "normal socket 1", "normal socket 2"}
ids := make([]int64, 3)
ids[0] = channelz.RegisterSocket(&channelz.Socket{SocketType: channelz.SocketTypeListen, Parent: svrID, RefName: refNames[0]}).ID
ids[1] = channelz.RegisterSocket(&channelz.Socket{SocketType: channelz.SocketTypeNormal, Parent: svrID, RefName: refNames[1]}).ID
ids[2] = channelz.RegisterSocket(&channelz.Socket{SocketType: channelz.SocketTypeNormal, Parent: svrID, RefName: refNames[2]}).ID
for _, id := range ids {
defer channelz.RemoveEntry(id)
}
svr := newCZServer()
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel()
// Make GetServerSockets with startID = ids[1]+1, so socket-1 won't be
// included in the response.
resp, _ := svr.GetServerSockets(ctx, &channelzpb.GetServerSocketsRequest{ServerId: svrID.ID, StartSocketId: ids[1] + 1})
if !resp.GetEnd() {
t.Fatalf("resp.GetEnd() want: true, got: %v", resp.GetEnd())
}
// GetServerSockets only return normal socket-2, socket-1 should be
// filtered by start ID.
want := map[int64]string{
ids[2]: refNames[2],
}
if !cmp.Equal(convertSocketRefSliceToMap(resp.GetSocketRef()), want) {
t.Fatalf("GetServerSockets want: %#v, got: %#v", want, resp.GetSocketRef())
}
}
var logger = grpclog.Component("channelz")
func (s) TestGetChannel(t *testing.T) {
refNames := []string{"top channel 1", "nested channel 1", "sub channel 2", "nested channel 3"}
cids := make([]*channelz.Channel, 3)
cids[0] = channelz.RegisterChannel(nil, refNames[0])
channelz.AddTraceEvent(logger, cids[0], 0, &channelz.TraceEvent{
Desc: "Channel Created",
Severity: channelz.CtInfo,
})
cids[1] = channelz.RegisterChannel(cids[0], refNames[1])
channelz.AddTraceEvent(logger, cids[1], 0, &channelz.TraceEvent{
Desc: "Channel Created",
Severity: channelz.CtInfo,
Parent: &channelz.TraceEvent{
Desc: fmt.Sprintf("Nested Channel(id:%d) created", cids[1].ID),
Severity: channelz.CtInfo,
},
})
subChan := channelz.RegisterSubChannel(cids[0], refNames[2])
channelz.AddTraceEvent(logger, subChan, 0, &channelz.TraceEvent{
Desc: "SubChannel Created",
Severity: channelz.CtInfo,
Parent: &channelz.TraceEvent{
Desc: fmt.Sprintf("SubChannel(id:%d) created", subChan.ID),
Severity: channelz.CtInfo,
},
})
defer channelz.RemoveEntry(subChan.ID)
cids[2] = channelz.RegisterChannel(cids[1], refNames[3])
channelz.AddTraceEvent(logger, cids[2], 0, &channelz.TraceEvent{
Desc: "Channel Created",
Severity: channelz.CtInfo,
Parent: &channelz.TraceEvent{
Desc: fmt.Sprintf("Nested Channel(id:%d) created", cids[2].ID),
Severity: channelz.CtInfo,
},
})
channelz.AddTraceEvent(logger, cids[0], 0, &channelz.TraceEvent{
Desc: fmt.Sprintf("Channel Connectivity change to %v", connectivity.Ready),
Severity: channelz.CtInfo,
})
channelz.AddTraceEvent(logger, cids[0], 0, &channelz.TraceEvent{
Desc: "Resolver returns an empty address list",
Severity: channelz.CtWarning,
})
for _, id := range cids {
defer channelz.RemoveEntry(id.ID)
}
svr := newCZServer()
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel()
resp, _ := svr.GetChannel(ctx, &channelzpb.GetChannelRequest{ChannelId: cids[0].ID})
metrics := resp.GetChannel()
subChans := metrics.GetSubchannelRef()
if len(subChans) != 1 || subChans[0].GetName() != refNames[2] || subChans[0].GetSubchannelId() != subChan.ID {
t.Fatalf("metrics.GetSubChannelRef() want %#v, got %#v", []*channelzpb.SubchannelRef{{SubchannelId: subChan.ID, Name: refNames[2]}}, subChans)
}
nestedChans := metrics.GetChannelRef()
if len(nestedChans) != 1 || nestedChans[0].GetName() != refNames[1] || nestedChans[0].GetChannelId() != cids[1].ID {
t.Fatalf("metrics.GetChannelRef() want %#v, got %#v", []*channelzpb.ChannelRef{{ChannelId: cids[1].ID, Name: refNames[1]}}, nestedChans)
}
trace := metrics.GetData().GetTrace()
want := []struct {
desc string
severity channelzpb.ChannelTraceEvent_Severity
childID int64
childRef string
}{
{desc: "Channel Created", severity: channelzpb.ChannelTraceEvent_CT_INFO},
{desc: fmt.Sprintf("Nested Channel(id:%d) created", cids[1].ID), severity: channelzpb.ChannelTraceEvent_CT_INFO, childID: cids[1].ID, childRef: refNames[1]},
{desc: fmt.Sprintf("SubChannel(id:%d) created", subChan.ID), severity: channelzpb.ChannelTraceEvent_CT_INFO, childID: subChan.ID, childRef: refNames[2]},
{desc: fmt.Sprintf("Channel Connectivity change to %v", connectivity.Ready), severity: channelzpb.ChannelTraceEvent_CT_INFO},
{desc: "Resolver returns an empty address list", severity: channelzpb.ChannelTraceEvent_CT_WARNING},
}
for i, e := range trace.Events {
if !strings.Contains(e.GetDescription(), want[i].desc) {
t.Fatalf("trace: GetDescription want %#v, got %#v", want[i].desc, e.GetDescription())
}
if e.GetSeverity() != want[i].severity {
t.Fatalf("trace: GetSeverity want %#v, got %#v", want[i].severity, e.GetSeverity())
}
if want[i].childID == 0 && (e.GetChannelRef() != nil || e.GetSubchannelRef() != nil) {
t.Fatalf("trace: GetChannelRef() should return nil, as there is no reference")
}
if e.GetChannelRef().GetChannelId() != want[i].childID || e.GetChannelRef().GetName() != want[i].childRef {
if e.GetSubchannelRef().GetSubchannelId() != want[i].childID || e.GetSubchannelRef().GetName() != want[i].childRef {
t.Fatalf("trace: GetChannelRef/GetSubchannelRef want (child ID: %d, child name: %q), got %#v and %#v", want[i].childID, want[i].childRef, e.GetChannelRef(), e.GetSubchannelRef())
}
}
}
resp, _ = svr.GetChannel(ctx, &channelzpb.GetChannelRequest{ChannelId: cids[1].ID})
metrics = resp.GetChannel()
nestedChans = metrics.GetChannelRef()
if len(nestedChans) != 1 || nestedChans[0].GetName() != refNames[3] || nestedChans[0].GetChannelId() != cids[2].ID {
t.Fatalf("metrics.GetChannelRef() want %#v, got %#v", []*channelzpb.ChannelRef{{ChannelId: cids[2].ID, Name: refNames[3]}}, nestedChans)
}
}
func (s) TestGetSubChannel(t *testing.T) {
var (
subchanCreated = "SubChannel Created"
subchanConnectivityChange = fmt.Sprintf("Subchannel Connectivity change to %v", connectivity.Ready)
subChanPickNewAddress = fmt.Sprintf("Subchannel picks a new address %q to connect", "0.0.0.0")
)
refNames := []string{"top channel 1", "sub channel 1", "socket 1", "socket 2"}
chann := channelz.RegisterChannel(nil, refNames[0])
defer channelz.RemoveEntry(chann.ID)
channelz.AddTraceEvent(logger, chann, 0, &channelz.TraceEvent{
Desc: "Channel Created",
Severity: channelz.CtInfo,
})
subChan := channelz.RegisterSubChannel(chann, refNames[1])
defer channelz.RemoveEntry(subChan.ID)
channelz.AddTraceEvent(logger, subChan, 0, &channelz.TraceEvent{
Desc: subchanCreated,
Severity: channelz.CtInfo,
Parent: &channelz.TraceEvent{
Desc: fmt.Sprintf("Nested Channel(id:%d) created", chann.ID),
Severity: channelz.CtInfo,
},
})
skt1 := channelz.RegisterSocket(&channelz.Socket{SocketType: channelz.SocketTypeNormal, Parent: subChan, RefName: refNames[2]})
defer channelz.RemoveEntry(skt1.ID)
skt2 := channelz.RegisterSocket(&channelz.Socket{SocketType: channelz.SocketTypeNormal, Parent: subChan, RefName: refNames[3]})
defer channelz.RemoveEntry(skt2.ID)
channelz.AddTraceEvent(logger, subChan, 0, &channelz.TraceEvent{
Desc: subchanConnectivityChange,
Severity: channelz.CtInfo,
})
channelz.AddTraceEvent(logger, subChan, 0, &channelz.TraceEvent{
Desc: subChanPickNewAddress,
Severity: channelz.CtInfo,
})
svr := newCZServer()
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel()
resp, _ := svr.GetSubchannel(ctx, &channelzpb.GetSubchannelRequest{SubchannelId: subChan.ID})
metrics := resp.GetSubchannel()
want := map[int64]string{
skt1.ID: refNames[2],
skt2.ID: refNames[3],
}
if !cmp.Equal(convertSocketRefSliceToMap(metrics.GetSocketRef()), want) {
t.Fatalf("metrics.GetSocketRef() want %#v: got: %#v", want, metrics.GetSocketRef())
}
trace := metrics.GetData().GetTrace()
wantTrace := []struct {
desc string
severity channelzpb.ChannelTraceEvent_Severity
childID int64
childRef string
}{
{desc: subchanCreated, severity: channelzpb.ChannelTraceEvent_CT_INFO},
{desc: subchanConnectivityChange, severity: channelzpb.ChannelTraceEvent_CT_INFO},
{desc: subChanPickNewAddress, severity: channelzpb.ChannelTraceEvent_CT_INFO},
}
for i, e := range trace.Events {
if e.GetDescription() != wantTrace[i].desc {
t.Fatalf("trace: GetDescription want %#v, got %#v", wantTrace[i].desc, e.GetDescription())
}
if e.GetSeverity() != wantTrace[i].severity {
t.Fatalf("trace: GetSeverity want %#v, got %#v", wantTrace[i].severity, e.GetSeverity())
}
if wantTrace[i].childID == 0 && (e.GetChannelRef() != nil || e.GetSubchannelRef() != nil) {
t.Fatalf("trace: GetChannelRef() should return nil, as there is no reference")
}
if e.GetChannelRef().GetChannelId() != wantTrace[i].childID || e.GetChannelRef().GetName() != wantTrace[i].childRef {
if e.GetSubchannelRef().GetSubchannelId() != wantTrace[i].childID || e.GetSubchannelRef().GetName() != wantTrace[i].childRef {
t.Fatalf("trace: GetChannelRef/GetSubchannelRef want (child ID: %d, child name: %q), got %#v and %#v", wantTrace[i].childID, wantTrace[i].childRef, e.GetChannelRef(), e.GetSubchannelRef())
}
}
}
}
type czSocket struct {
streamsStarted int64
streamsSucceeded int64
streamsFailed int64
messagesSent int64
messagesReceived int64
keepAlivesSent int64
lastLocalStreamCreatedTimestamp time.Time
lastRemoteStreamCreatedTimestamp time.Time
lastMessageSentTimestamp time.Time
lastMessageReceivedTimestamp time.Time
localFlowControlWindow int64
remoteFlowControlWindow int64
localAddr net.Addr
remoteAddr net.Addr
remoteName string
socketOptions *channelz.SocketOptionData
security credentials.ChannelzSecurityValue
}
func newSocket(cs czSocket) *channelz.Socket {
if cs.lastLocalStreamCreatedTimestamp.IsZero() {
cs.lastLocalStreamCreatedTimestamp = time.Unix(0, 0)
}
if cs.lastRemoteStreamCreatedTimestamp.IsZero() {
cs.lastRemoteStreamCreatedTimestamp = time.Unix(0, 0)
}
if cs.lastMessageSentTimestamp.IsZero() {
cs.lastMessageSentTimestamp = time.Unix(0, 0)
}
if cs.lastMessageReceivedTimestamp.IsZero() {
cs.lastMessageReceivedTimestamp = time.Unix(0, 0)
}
s := &channelz.Socket{
LocalAddr: cs.localAddr,
RemoteAddr: cs.remoteAddr,
RemoteName: cs.remoteName,
SocketOptions: cs.socketOptions,
Security: cs.security,
}
s.SocketMetrics.StreamsStarted.Store(cs.streamsStarted)
s.SocketMetrics.StreamsSucceeded.Store(cs.streamsSucceeded)
s.SocketMetrics.StreamsFailed.Store(cs.streamsFailed)
s.SocketMetrics.MessagesSent.Store(cs.messagesSent)
s.SocketMetrics.MessagesReceived.Store(cs.messagesReceived)
s.SocketMetrics.KeepAlivesSent.Store(cs.keepAlivesSent)
s.SocketMetrics.LastLocalStreamCreatedTimestamp.Store(cs.lastLocalStreamCreatedTimestamp.UnixNano())
s.SocketMetrics.LastRemoteStreamCreatedTimestamp.Store(cs.lastRemoteStreamCreatedTimestamp.UnixNano())
s.SocketMetrics.LastMessageSentTimestamp.Store(cs.lastMessageSentTimestamp.UnixNano())
s.SocketMetrics.LastMessageReceivedTimestamp.Store(cs.lastMessageReceivedTimestamp.UnixNano())
s.EphemeralMetrics = func() *channelz.EphemeralSocketMetrics {
return &channelz.EphemeralSocketMetrics{
LocalFlowControlWindow: cs.localFlowControlWindow,
RemoteFlowControlWindow: cs.remoteFlowControlWindow,
}
}
return s
}
type OtherChannelzSecurityValue struct {
LocalCertificate []byte `protobuf:"bytes,1,opt,name=local_certificate,json=localCertificate,proto3" json:"local_certificate,omitempty"`
RemoteCertificate []byte `protobuf:"bytes,2,opt,name=remote_certificate,json=remoteCertificate,proto3" json:"remote_certificate,omitempty"`
}
func (x *OtherChannelzSecurityValue) Reset() {
*x = OtherChannelzSecurityValue{}
}
func (x *OtherChannelzSecurityValue) String() string {
return prototext.Format(x)
}
func (*OtherChannelzSecurityValue) ProtoMessage() {}
func (x OtherChannelzSecurityValue) ProtoReflect() protoreflect.Message {
const s = `
name: "service_test.proto"
syntax: "proto3"
package: "grpc.credentials",
message_type: [{
name: "OtherChannelzSecurityValue"
field: [
{name:"local_certificate" number:1 type:TYPE_BYTES},
{name:"remote_certificate" number:2 type:TYPE_BYTES}
]
}]
`
pb := new(descriptorpb.FileDescriptorProto)
if err := prototext.Unmarshal([]byte(s), pb); err != nil {
panic(err)
}
fd, err := protodesc.NewFile(pb, nil)
if err != nil {
panic(err)
}
md := fd.Messages().Get(0)
mt := dynamicpb.NewMessageType(md)
return mt.New()
}
func (s) TestGetSocket(t *testing.T) {
ss := []*channelz.Socket{newSocket(czSocket{
streamsStarted: 10,
streamsSucceeded: 2,
streamsFailed: 3,
messagesSent: 20,
messagesReceived: 10,
keepAlivesSent: 2,
lastLocalStreamCreatedTimestamp: time.Unix(0, 0),
lastRemoteStreamCreatedTimestamp: time.Unix(1, 0),
lastMessageSentTimestamp: time.Unix(2, 0),
lastMessageReceivedTimestamp: time.Unix(3, 0),
localFlowControlWindow: 65536,
remoteFlowControlWindow: 1024,
localAddr: &net.TCPAddr{IP: netip.MustParseAddr("1.0.0.1").AsSlice(), Port: 10001},
remoteAddr: &net.TCPAddr{IP: netip.MustParseAddr("12.0.0.1").AsSlice(), Port: 10002},
remoteName: "remote.remote",
}), newSocket(czSocket{
streamsStarted: 10,
streamsSucceeded: 2,
streamsFailed: 3,
messagesSent: 20,
messagesReceived: 10,
keepAlivesSent: 2,
lastLocalStreamCreatedTimestamp: time.Unix(0, 0),
lastRemoteStreamCreatedTimestamp: time.Unix(5, 0),
lastMessageSentTimestamp: time.Unix(6, 0),
lastMessageReceivedTimestamp: time.Unix(7, 0),
localFlowControlWindow: 65536,
remoteFlowControlWindow: 1024,
localAddr: &net.UnixAddr{Name: "file.path", Net: "unix"},
remoteAddr: &net.UnixAddr{Name: "another.path", Net: "unix"},
remoteName: "remote.remote",
}), newSocket(czSocket{
streamsStarted: 5,
streamsSucceeded: 2,
streamsFailed: 3,
messagesSent: 20,
messagesReceived: 10,
keepAlivesSent: 2,
lastLocalStreamCreatedTimestamp: time.Unix(10, 10),
lastRemoteStreamCreatedTimestamp: time.Unix(0, 0),
lastMessageSentTimestamp: time.Unix(0, 0),
lastMessageReceivedTimestamp: time.Unix(0, 0),
localFlowControlWindow: 65536,
remoteFlowControlWindow: 10240,
localAddr: &net.IPAddr{IP: netip.MustParseAddr("1.0.0.1").AsSlice()},
remoteAddr: &net.IPAddr{IP: netip.MustParseAddr("9.0.0.1").AsSlice()},
remoteName: "",
}), newSocket(czSocket{
localAddr: &net.TCPAddr{IP: netip.MustParseAddr("127.0.0.1").AsSlice(), Port: 10001},
}), newSocket(czSocket{
security: &credentials.TLSChannelzSecurityValue{
StandardName: "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
RemoteCertificate: []byte{48, 130, 2, 156, 48, 130, 2, 5, 160},
},
}), newSocket(czSocket{
security: &credentials.OtherChannelzSecurityValue{
Name: "XXXX",
},
}), newSocket(czSocket{
security: &credentials.OtherChannelzSecurityValue{
Name: "YYYY",
Value: OtherChannelzSecurityValue{
LocalCertificate: []byte{1, 2, 3},
RemoteCertificate: []byte{4, 5, 6},
},
},
}),
}
otherSecVal, err := anypb.New(ss[6].Security.(*credentials.OtherChannelzSecurityValue).Value)
if err != nil {
t.Fatal("Error marshalling proto:", err)
}
svr := newCZServer()
skts := make([]*channelz.Socket, len(ss))
svrID := channelz.RegisterServer("")
defer channelz.RemoveEntry(svrID.ID)
for i, s := range ss {
s.Parent = svrID
s.RefName = strconv.Itoa(i)
skts[i] = channelz.RegisterSocket(s)
defer channelz.RemoveEntry(skts[i].ID)
}
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel()
emptyData := `data: {
last_local_stream_created_timestamp: {seconds: 0 nanos: 0}
last_remote_stream_created_timestamp: {seconds: 0 nanos: 0}
last_message_sent_timestamp: {seconds: 0 nanos: 0}
last_message_received_timestamp: {seconds: 0 nanos: 0}
local_flow_control_window: { value: 0 }
remote_flow_control_window: { value: 0 }
}`
want := []string{`
ref: {socket_id: ` + fmt.Sprint(skts[0].ID) + ` name: "0" }
data: {
streams_started: 10
streams_succeeded: 2
streams_failed: 3
messages_sent: 20
messages_received: 10
keep_alives_sent: 2
last_local_stream_created_timestamp: {seconds: 0 nanos: 0}
last_remote_stream_created_timestamp: {seconds: 1 nanos: 0}
last_message_sent_timestamp: {seconds: 2 nanos: 0}
last_message_received_timestamp: {seconds: 3 nanos: 0}
local_flow_control_window: { value: 65536 }
remote_flow_control_window: { value: 1024 }
}
local: { tcpip_address: { ip_address: "` + addr(skts[0].LocalAddr) + `" port: 10001 } }
remote: { tcpip_address: { ip_address: "` + addr(skts[0].RemoteAddr) + `" port: 10002 } }
remote_name: "remote.remote"`,
`
ref: {socket_id: ` + fmt.Sprint(skts[1].ID) + ` name: "1" }
data: {
streams_started: 10
streams_succeeded: 2
streams_failed: 3
messages_sent: 20
messages_received: 10
keep_alives_sent: 2
last_local_stream_created_timestamp: {seconds: 0 nanos: 0}
last_remote_stream_created_timestamp: {seconds: 5 nanos: 0}
last_message_sent_timestamp: {seconds: 6 nanos: 0}
last_message_received_timestamp: {seconds: 7 nanos: 0}
local_flow_control_window: { value: 65536 }
remote_flow_control_window: { value: 1024 }
}
local: { uds_address { filename: "file.path" } }
remote: { uds_address { filename: "another.path" } }
remote_name: "remote.remote"`,
`
ref: {socket_id: ` + fmt.Sprint(skts[2].ID) + ` name: "2" }
data: {
streams_started: 5
streams_succeeded: 2
streams_failed: 3
messages_sent: 20
messages_received: 10
keep_alives_sent: 2
last_local_stream_created_timestamp: {seconds: 10 nanos: 10}
last_remote_stream_created_timestamp: {seconds: 0 nanos: 0}
last_message_sent_timestamp: {seconds: 0 nanos: 0}
last_message_received_timestamp: {seconds: 0 nanos: 0}
local_flow_control_window: { value: 65536 }
remote_flow_control_window: { value: 10240 }
}
local: { tcpip_address: { ip_address: "` + addr(skts[2].LocalAddr) + `" } }
remote: { tcpip_address: { ip_address: "` + addr(skts[2].RemoteAddr) + `" } }
remote_name: ""`,
`
ref: {socket_id: ` + fmt.Sprint(skts[3].ID) + ` name: "3" }
local: { tcpip_address: { ip_address: "` + addr(skts[3].LocalAddr) + `" port: 10001 } }
` + emptyData,
`
ref: {socket_id: ` + fmt.Sprint(skts[4].ID) + ` name: "4" }
security: { tls: {
standard_name: "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
remote_certificate: "\x30\x82\x02\x9c\x30\x82\x02\x05\xa0"
} }
` + emptyData,
`
ref: {socket_id: ` + fmt.Sprint(skts[5].ID) + ` name: "5" }
security: { other: { name: "XXXX" } }
` + emptyData,
`
ref: {socket_id: ` + fmt.Sprint(skts[6].ID) + ` name: "6" }
security: { other: {
name: "YYYY"
value: {
type_url: "type.googleapis.com/grpc.credentials.OtherChannelzSecurityValue"
value: "` + escape(otherSecVal.Value) + `"
}
} }
` + emptyData,
}
for i := range ss {
resp, _ := svr.GetSocket(ctx, &channelzpb.GetSocketRequest{SocketId: skts[i].ID})
w := &channelzpb.Socket{}
if err := prototext.Unmarshal([]byte(want[i]), w); err != nil {
t.Fatalf("Error unmarshalling %q: %v", want[i], err)
}
if diff := cmp.Diff(resp.GetSocket(), w, protocmp.Transform()); diff != "" {
t.Fatalf("Socket %v did not match expected. -got +want: %v", i, diff)
}
}
}
func escape(bs []byte) string {
ret := ""
for _, b := range bs {
ret += fmt.Sprintf("\\x%02x", b)
}
return ret
}
func addr(a net.Addr) string {
switch a := a.(type) {
case *net.TCPAddr:
return escape([]byte(a.IP))
case *net.IPAddr:
return escape([]byte(a.IP))
}
return ""
}