github.com/aporeto-inc/trireme-lib@v10.358.0+incompatible/controller/pkg/remoteenforcer/internal/client/statsclient/client.go (about)

     1  package statsclient
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"os"
     7  	"time"
     8  
     9  	"go.aporeto.io/enforcerd/trireme-lib/collector"
    10  	"go.aporeto.io/enforcerd/trireme-lib/controller/constants"
    11  	"go.aporeto.io/enforcerd/trireme-lib/controller/internal/enforcer/utils/rpcwrapper"
    12  	"go.aporeto.io/enforcerd/trireme-lib/controller/pkg/remoteenforcer/internal/client"
    13  	"go.aporeto.io/enforcerd/trireme-lib/controller/pkg/remoteenforcer/internal/statscollector"
    14  	"go.uber.org/zap"
    15  )
    16  
    17  const (
    18  	defaultStatsIntervalMiliseconds = 1000
    19  	defaultUserRetention            = 10
    20  	statsContextID                  = "UNUSED"
    21  	statsRPCCommand                 = "ProxyRPCServer.PostStats"
    22  )
    23  
    24  // statsClient  This is the struct for storing state for the rpc client
    25  // which reports flow stats back to the controller process
    26  type statsClient struct {
    27  	collector     statscollector.Collector
    28  	rpchdl        *rpcwrapper.RPCWrapper
    29  	secret        string
    30  	statsChannel  string
    31  	statsInterval time.Duration
    32  	userRetention time.Duration
    33  	stop          chan bool
    34  }
    35  
    36  // NewStatsClient initializes a new stats client
    37  func NewStatsClient(cr statscollector.Collector) (client.Reporter, error) {
    38  
    39  	sc := &statsClient{
    40  		collector:     cr,
    41  		rpchdl:        rpcwrapper.NewRPCWrapper(),
    42  		secret:        os.Getenv(constants.EnvStatsSecret),
    43  		statsChannel:  os.Getenv(constants.EnvStatsChannel),
    44  		statsInterval: defaultStatsIntervalMiliseconds * time.Millisecond,
    45  		userRetention: defaultUserRetention * time.Minute,
    46  		stop:          make(chan bool),
    47  	}
    48  
    49  	if sc.statsChannel == "" {
    50  		return nil, errors.New("no path to stats socket provided")
    51  	}
    52  
    53  	if sc.secret == "" {
    54  		return nil, errors.New("no secret provided for stats channel")
    55  	}
    56  
    57  	return sc, nil
    58  }
    59  
    60  // sendStats  async function which makes a rpc call to send stats every STATS_INTERVAL
    61  func (s *statsClient) sendStats(ctx context.Context) {
    62  
    63  	ticker := time.NewTicker(s.statsInterval)
    64  	userTicker := time.NewTicker(s.userRetention)
    65  	// nolint: gosimple
    66  	for {
    67  		select {
    68  		case <-ticker.C:
    69  
    70  			flows := s.collector.GetFlowRecords()
    71  			users := s.collector.GetUserRecords()
    72  			if flows == nil && users == nil {
    73  				continue
    74  			}
    75  
    76  			s.sendRequest(flows, users)
    77  		case <-userTicker.C:
    78  			s.collector.FlushUserCache()
    79  		case <-ctx.Done():
    80  			return
    81  		}
    82  	}
    83  
    84  }
    85  
    86  func (s *statsClient) sendRequest(flows map[uint64]*collector.FlowRecord, users map[string]*collector.UserRecord) {
    87  
    88  	request := rpcwrapper.Request{
    89  		Payload: &rpcwrapper.StatsPayload{
    90  			Flows: flows,
    91  			Users: users,
    92  		},
    93  	}
    94  
    95  	if err := s.rpchdl.RemoteCall(
    96  		statsContextID,
    97  		statsRPCCommand,
    98  		&request,
    99  		&rpcwrapper.Response{},
   100  	); err != nil {
   101  		zap.L().Error("RPC failure in sending statistics: Unable to send flows", zap.Error(err))
   102  	}
   103  }
   104  
   105  // Send sends all the stats from the cache
   106  func (s *statsClient) Send() error {
   107  
   108  	flows := s.collector.GetFlowRecords()
   109  	users := s.collector.GetUserRecords()
   110  	if flows == nil && users == nil {
   111  		zap.L().Debug("Flows and UserRecords are nil while sending stats to collector")
   112  		return nil
   113  	}
   114  
   115  	s.sendRequest(flows, users)
   116  	return nil
   117  }
   118  
   119  // Start This is an private function called by the remoteenforcer to connect back
   120  // to the controller over a stats channel
   121  func (s *statsClient) Run(ctx context.Context) error {
   122  	if err := s.rpchdl.NewRPCClient(statsContextID, s.statsChannel, s.secret); err != nil {
   123  		zap.L().Error("Stats RPC client cannot connect", zap.Error(err))
   124  		return err
   125  	}
   126  
   127  	go s.sendStats(ctx)
   128  
   129  	return nil
   130  }