mirror of https://github.com/grpc/grpc-go.git
804 lines
29 KiB
Go
804 lines
29 KiB
Go
/*
|
|
*
|
|
* 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 ""
|
|
}
|