google.golang.org/grpc@v1.62.1/channelz/service/service_sktopt_test.go (about) 1 //go:build linux && (386 || amd64) 2 // +build linux 3 // +build 386 amd64 4 5 /* 6 * 7 * Copyright 2018 gRPC authors. 8 * 9 * Licensed under the Apache License, Version 2.0 (the "License"); 10 * you may not use this file except in compliance with the License. 11 * You may obtain a copy of the License at 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, software 16 * distributed under the License is distributed on an "AS IS" BASIS, 17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 * See the License for the specific language governing permissions and 19 * limitations under the License. 20 * 21 */ 22 23 // SocketOptions is only supported on linux system. The functions defined in 24 // this file are to parse the socket option field and the test is specifically 25 // to verify the behavior of socket option parsing. 26 27 package service 28 29 import ( 30 "context" 31 "strconv" 32 "testing" 33 34 "github.com/golang/protobuf/ptypes" 35 "github.com/google/go-cmp/cmp" 36 "golang.org/x/sys/unix" 37 "google.golang.org/grpc/internal/channelz" 38 "google.golang.org/protobuf/testing/protocmp" 39 40 durpb "github.com/golang/protobuf/ptypes/duration" 41 channelzpb "google.golang.org/grpc/channelz/grpc_channelz_v1" 42 ) 43 44 func init() { 45 // Assign protoToSocketOption to protoToSocketOpt in order to enable socket option 46 // data conversion from proto message to channelz defined struct. 47 protoToSocketOpt = protoToSocketOption 48 } 49 50 func convertToDuration(d *durpb.Duration) (sec int64, usec int64) { 51 if d != nil { 52 if dur, err := ptypes.Duration(d); err == nil { 53 sec = int64(int64(dur) / 1e9) 54 usec = (int64(dur) - sec*1e9) / 1e3 55 } 56 } 57 return 58 } 59 60 func protoToLinger(protoLinger *channelzpb.SocketOptionLinger) *unix.Linger { 61 linger := &unix.Linger{} 62 if protoLinger.GetActive() { 63 linger.Onoff = 1 64 } 65 lv, _ := convertToDuration(protoLinger.GetDuration()) 66 linger.Linger = int32(lv) 67 return linger 68 } 69 70 func protoToSocketOption(skopts []*channelzpb.SocketOption) *channelz.SocketOptionData { 71 skdata := &channelz.SocketOptionData{} 72 for _, opt := range skopts { 73 switch opt.GetName() { 74 case "SO_LINGER": 75 protoLinger := &channelzpb.SocketOptionLinger{} 76 err := ptypes.UnmarshalAny(opt.GetAdditional(), protoLinger) 77 if err == nil { 78 skdata.Linger = protoToLinger(protoLinger) 79 } 80 case "SO_RCVTIMEO": 81 protoTimeout := &channelzpb.SocketOptionTimeout{} 82 err := ptypes.UnmarshalAny(opt.GetAdditional(), protoTimeout) 83 if err == nil { 84 skdata.RecvTimeout = protoToTime(protoTimeout) 85 } 86 case "SO_SNDTIMEO": 87 protoTimeout := &channelzpb.SocketOptionTimeout{} 88 err := ptypes.UnmarshalAny(opt.GetAdditional(), protoTimeout) 89 if err == nil { 90 skdata.SendTimeout = protoToTime(protoTimeout) 91 } 92 case "TCP_INFO": 93 tcpi := &channelzpb.SocketOptionTcpInfo{} 94 err := ptypes.UnmarshalAny(opt.GetAdditional(), tcpi) 95 if err == nil { 96 skdata.TCPInfo = &unix.TCPInfo{ 97 State: uint8(tcpi.TcpiState), 98 Ca_state: uint8(tcpi.TcpiCaState), 99 Retransmits: uint8(tcpi.TcpiRetransmits), 100 Probes: uint8(tcpi.TcpiProbes), 101 Backoff: uint8(tcpi.TcpiBackoff), 102 Options: uint8(tcpi.TcpiOptions), 103 Rto: tcpi.TcpiRto, 104 Ato: tcpi.TcpiAto, 105 Snd_mss: tcpi.TcpiSndMss, 106 Rcv_mss: tcpi.TcpiRcvMss, 107 Unacked: tcpi.TcpiUnacked, 108 Sacked: tcpi.TcpiSacked, 109 Lost: tcpi.TcpiLost, 110 Retrans: tcpi.TcpiRetrans, 111 Fackets: tcpi.TcpiFackets, 112 Last_data_sent: tcpi.TcpiLastDataSent, 113 Last_ack_sent: tcpi.TcpiLastAckSent, 114 Last_data_recv: tcpi.TcpiLastDataRecv, 115 Last_ack_recv: tcpi.TcpiLastAckRecv, 116 Pmtu: tcpi.TcpiPmtu, 117 Rcv_ssthresh: tcpi.TcpiRcvSsthresh, 118 Rtt: tcpi.TcpiRtt, 119 Rttvar: tcpi.TcpiRttvar, 120 Snd_ssthresh: tcpi.TcpiSndSsthresh, 121 Snd_cwnd: tcpi.TcpiSndCwnd, 122 Advmss: tcpi.TcpiAdvmss, 123 Reordering: tcpi.TcpiReordering} 124 } 125 } 126 } 127 return skdata 128 } 129 130 func (s) TestGetSocketOptions(t *testing.T) { 131 ss := []*dummySocket{ 132 { 133 socketOptions: &channelz.SocketOptionData{ 134 Linger: &unix.Linger{Onoff: 1, Linger: 2}, 135 RecvTimeout: &unix.Timeval{Sec: 10, Usec: 1}, 136 SendTimeout: &unix.Timeval{}, 137 TCPInfo: &unix.TCPInfo{State: 1}, 138 }, 139 }, 140 } 141 svr := newCZServer() 142 ids := make([]*channelz.Identifier, len(ss)) 143 svrID := channelz.RegisterServer(&dummyServer{}, "") 144 defer channelz.RemoveEntry(svrID) 145 for i, s := range ss { 146 ids[i], _ = channelz.RegisterNormalSocket(s, svrID, strconv.Itoa(i)) 147 defer channelz.RemoveEntry(ids[i]) 148 } 149 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 150 defer cancel() 151 for i, s := range ss { 152 resp, _ := svr.GetSocket(ctx, &channelzpb.GetSocketRequest{SocketId: ids[i].Int()}) 153 got, want := resp.GetSocket().GetRef(), &channelzpb.SocketRef{SocketId: ids[i].Int(), Name: strconv.Itoa(i)} 154 if !cmp.Equal(got, want, protocmp.Transform()) { 155 t.Fatalf("resp.GetSocket() returned metrics.GetRef() = %#v, want %#v", got, want) 156 } 157 socket, err := socketProtoToStruct(resp.GetSocket()) 158 if err != nil { 159 t.Fatal(err) 160 } 161 if diff := cmp.Diff(s, socket, protocmp.Transform(), cmp.AllowUnexported(dummySocket{})); diff != "" { 162 t.Fatalf("unexpected socket, diff (-want +got):\n%s", diff) 163 } 164 } 165 }