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  }