From 3a927c28e1d333d3275d1769a4924e1f66de900c Mon Sep 17 00:00:00 2001 From: Bryce Kahle Date: Wed, 20 Nov 2024 15:15:32 -0800 Subject: [PATCH] move service discovery to simpler sysprobe client --- .../corechecks/servicediscovery/impl_linux.go | 56 ++++++--- .../servicediscovery/impl_linux_test.go | 12 +- pkg/languagedetection/detector.go | 63 ++++++++-- pkg/languagedetection/detector_linux_test.go | 1 - pkg/process/net/common.go | 72 ----------- pkg/process/net/common_linux.go | 16 ++- pkg/process/net/common_unsupported.go | 12 -- pkg/process/net/common_windows.go | 13 +- pkg/process/net/mocks/sys_probe_util.go | 118 ------------------ pkg/process/net/shared.go | 4 - 10 files changed, 107 insertions(+), 260 deletions(-) diff --git a/pkg/collector/corechecks/servicediscovery/impl_linux.go b/pkg/collector/corechecks/servicediscovery/impl_linux.go index c9d4e66bd0406..617b740990922 100644 --- a/pkg/collector/corechecks/servicediscovery/impl_linux.go +++ b/pkg/collector/corechecks/servicediscovery/impl_linux.go @@ -8,12 +8,16 @@ package servicediscovery import ( + "encoding/json" + "fmt" + "net/http" "time" + sysprobeclient "github.com/DataDog/datadog-agent/cmd/system-probe/api/client" + sysconfig "github.com/DataDog/datadog-agent/cmd/system-probe/config" "github.com/DataDog/datadog-agent/pkg/collector/corechecks/servicediscovery/model" "github.com/DataDog/datadog-agent/pkg/collector/corechecks/servicediscovery/servicetype" pkgconfigsetup "github.com/DataDog/datadog-agent/pkg/config/setup" - processnet "github.com/DataDog/datadog-agent/pkg/process/net" "github.com/DataDog/datadog-agent/pkg/util/log" ) @@ -24,38 +28,56 @@ func init() { } type linuxImpl struct { - getSysProbeClient processnet.SysProbeUtilGetter - time timer + getDiscoveryServices func(client *http.Client) (*model.ServicesResponse, error) + time timer ignoreCfg map[string]bool ignoreProcs map[int]bool aliveServices map[int]*serviceInfo potentialServices map[int]*serviceInfo + + sysProbeClient *http.Client } func newLinuxImpl(ignoreCfg map[string]bool) (osImpl, error) { return &linuxImpl{ - getSysProbeClient: processnet.GetRemoteSystemProbeUtil, - time: realTime{}, - ignoreCfg: ignoreCfg, - ignoreProcs: make(map[int]bool), - aliveServices: make(map[int]*serviceInfo), - potentialServices: make(map[int]*serviceInfo), + getDiscoveryServices: getDiscoveryServices, + time: realTime{}, + ignoreCfg: ignoreCfg, + ignoreProcs: make(map[int]bool), + aliveServices: make(map[int]*serviceInfo), + potentialServices: make(map[int]*serviceInfo), + sysProbeClient: sysprobeclient.Get(pkgconfigsetup.SystemProbe().GetString("system_probe_config.sysprobe_socket")), }, nil } -func (li *linuxImpl) DiscoverServices() (*discoveredServices, error) { - socket := pkgconfigsetup.SystemProbe().GetString("system_probe_config.sysprobe_socket") - sysProbe, err := li.getSysProbeClient(socket) +func getDiscoveryServices(client *http.Client) (*model.ServicesResponse, error) { + url := sysprobeclient.ModuleURL(sysconfig.DiscoveryModule, "/services") + req, err := http.NewRequest(http.MethodGet, url, nil) if err != nil { - return nil, errWithCode{ - err: err, - code: errorCodeSystemProbeConn, - } + return nil, err + } + + resp, err := client.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("got non-success status code: url: %s, status_code: %d", req.URL, resp.StatusCode) } - response, err := sysProbe.GetDiscoveryServices() + res := &model.ServicesResponse{} + if err := json.NewDecoder(resp.Body).Decode(res); err != nil { + return nil, err + } + return res, nil +} + +func (li *linuxImpl) DiscoverServices() (*discoveredServices, error) { + response, err := li.getDiscoveryServices(li.sysProbeClient) if err != nil { return nil, errWithCode{ err: err, diff --git a/pkg/collector/corechecks/servicediscovery/impl_linux_test.go b/pkg/collector/corechecks/servicediscovery/impl_linux_test.go index f3f7d4baaeb66..e6da859352596 100644 --- a/pkg/collector/corechecks/servicediscovery/impl_linux_test.go +++ b/pkg/collector/corechecks/servicediscovery/impl_linux_test.go @@ -9,6 +9,7 @@ package servicediscovery import ( "cmp" + "net/http" "testing" "time" @@ -22,8 +23,6 @@ import ( "github.com/DataDog/datadog-agent/pkg/aggregator/mocksender" "github.com/DataDog/datadog-agent/pkg/collector/corechecks/servicediscovery/apm" "github.com/DataDog/datadog-agent/pkg/collector/corechecks/servicediscovery/model" - "github.com/DataDog/datadog-agent/pkg/process/net" - netmocks "github.com/DataDog/datadog-agent/pkg/process/net/mocks" ) type testProc struct { @@ -601,19 +600,14 @@ func Test_linuxImpl(t *testing.T) { require.NotNil(t, check.os) for _, cr := range tc.checkRun { - mSysProbe := netmocks.NewSysProbeUtil(t) - mSysProbe.EXPECT().GetDiscoveryServices(). - Return(cr.servicesResp, nil). - Times(1) - _, mHostname := hostnameinterface.NewMock(hostnameinterface.MockHostname(host)) mTimer := NewMocktimer(ctrl) mTimer.EXPECT().Now().Return(cr.time).AnyTimes() // set mocks - check.os.(*linuxImpl).getSysProbeClient = func(_ string) (net.SysProbeUtil, error) { - return mSysProbe, nil + check.os.(*linuxImpl).getDiscoveryServices = func(_ *http.Client) (*model.ServicesResponse, error) { + return cr.servicesResp, nil } check.os.(*linuxImpl).time = mTimer check.sender.hostname = mHostname diff --git a/pkg/languagedetection/detector.go b/pkg/languagedetection/detector.go index 92ab7fe4cc4c5..50fbc7c19ffb5 100644 --- a/pkg/languagedetection/detector.go +++ b/pkg/languagedetection/detector.go @@ -7,15 +7,22 @@ package languagedetection import ( + "bytes" + "io" + "net/http" "regexp" "runtime" "strings" "time" + "google.golang.org/protobuf/proto" + + sysprobeclient "github.com/DataDog/datadog-agent/cmd/system-probe/api/client" + sysconfig "github.com/DataDog/datadog-agent/cmd/system-probe/config" "github.com/DataDog/datadog-agent/pkg/config/model" "github.com/DataDog/datadog-agent/pkg/languagedetection/internal/detectors" "github.com/DataDog/datadog-agent/pkg/languagedetection/languagemodels" - "github.com/DataDog/datadog-agent/pkg/process/net" + languagepb "github.com/DataDog/datadog-agent/pkg/proto/pbgo/languagedetection" "github.com/DataDog/datadog-agent/pkg/telemetry" "github.com/DataDog/datadog-agent/pkg/util/log" ) @@ -140,15 +147,8 @@ func DetectLanguage(procs []languagemodels.Process, sysprobeConfig model.Reader) }() log.Trace("[language detection] Requesting language from system probe") - util, err := net.GetRemoteSystemProbeUtil( - sysprobeConfig.GetString("system_probe_config.sysprobe_socket"), - ) - if err != nil { - log.Warn("[language detection] Failed to request language:", err) - return langs - } - - privilegedLangs, err := util.DetectLanguage(unknownPids) + sysprobeClient := sysprobeclient.Get(sysprobeConfig.GetString("system_probe_config.sysprobe_socket")) + privilegedLangs, err := detectLanguage(sysprobeClient, unknownPids) if err != nil { log.Warn("[language detection] Failed to request language:", err) return langs @@ -161,6 +161,49 @@ func DetectLanguage(procs []languagemodels.Process, sysprobeConfig model.Reader) return langs } +func detectLanguage(client *http.Client, pids []int32) ([]languagemodels.Language, error) { + procs := make([]*languagepb.Process, len(pids)) + for i, pid := range pids { + procs[i] = &languagepb.Process{Pid: pid} + } + reqBytes, err := proto.Marshal(&languagepb.DetectLanguageRequest{Processes: procs}) + if err != nil { + return nil, err + } + + url := sysprobeclient.ModuleURL(sysconfig.LanguageDetectionModule, "/detect") + req, err := http.NewRequest(http.MethodGet, url, bytes.NewBuffer(reqBytes)) + if err != nil { + return nil, err + } + + res, err := client.Do(req) + if err != nil { + return nil, err + } + defer res.Body.Close() + + resBody, err := io.ReadAll(res.Body) + if err != nil { + return nil, err + } + + var resProto languagepb.DetectLanguageResponse + err = proto.Unmarshal(resBody, &resProto) + if err != nil { + return nil, err + } + + langs := make([]languagemodels.Language, len(pids)) + for i, lang := range resProto.Languages { + langs[i] = languagemodels.Language{ + Name: languagemodels.LanguageName(lang.Name), + Version: lang.Version, + } + } + return langs, nil +} + func privilegedLanguageDetectionEnabled(sysProbeConfig model.Reader) bool { if sysProbeConfig == nil { return false diff --git a/pkg/languagedetection/detector_linux_test.go b/pkg/languagedetection/detector_linux_test.go index 7b2c7e092507a..850cfd8914eae 100644 --- a/pkg/languagedetection/detector_linux_test.go +++ b/pkg/languagedetection/detector_linux_test.go @@ -14,7 +14,6 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" "google.golang.org/protobuf/proto" diff --git a/pkg/process/net/common.go b/pkg/process/net/common.go index a6f2b4d52ebb4..b9f1b787d52ad 100644 --- a/pkg/process/net/common.go +++ b/pkg/process/net/common.go @@ -10,7 +10,6 @@ package net import ( "bytes" "context" - "encoding/json" "fmt" "io" "net" @@ -18,15 +17,11 @@ import ( "time" model "github.com/DataDog/agent-payload/v5/process" - "google.golang.org/protobuf/proto" - discoverymodel "github.com/DataDog/datadog-agent/pkg/collector/corechecks/servicediscovery/model" - "github.com/DataDog/datadog-agent/pkg/languagedetection/languagemodels" netEncoding "github.com/DataDog/datadog-agent/pkg/network/encoding/unmarshal" nppayload "github.com/DataDog/datadog-agent/pkg/networkpath/payload" procEncoding "github.com/DataDog/datadog-agent/pkg/process/encoding" reqEncoding "github.com/DataDog/datadog-agent/pkg/process/encoding/request" - languagepb "github.com/DataDog/datadog-agent/pkg/proto/pbgo/languagedetection" pbgo "github.com/DataDog/datadog-agent/pkg/proto/pbgo/process" "github.com/DataDog/datadog-agent/pkg/util/funcs" "github.com/DataDog/datadog-agent/pkg/util/log" @@ -287,73 +282,6 @@ func (r *RemoteSysProbeUtil) Register(clientID string) error { return nil } -//nolint:revive // TODO(PROC) Fix revive linter -func (r *RemoteSysProbeUtil) DetectLanguage(pids []int32) ([]languagemodels.Language, error) { - procs := make([]*languagepb.Process, len(pids)) - for i, pid := range pids { - procs[i] = &languagepb.Process{Pid: pid} - } - reqBytes, err := proto.Marshal(&languagepb.DetectLanguageRequest{Processes: procs}) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, languageDetectionURL, bytes.NewBuffer(reqBytes)) - if err != nil { - return nil, err - } - - res, err := r.httpClient.Do(req) - if err != nil { - return nil, err - } - defer res.Body.Close() - - resBody, err := io.ReadAll(res.Body) - if err != nil { - return nil, err - } - - var resProto languagepb.DetectLanguageResponse - err = proto.Unmarshal(resBody, &resProto) - if err != nil { - return nil, err - } - - langs := make([]languagemodels.Language, len(pids)) - for i, lang := range resProto.Languages { - langs[i] = languagemodels.Language{ - Name: languagemodels.LanguageName(lang.Name), - Version: lang.Version, - } - } - return langs, nil -} - -// GetDiscoveryServices returns service information from system-probe. -func (r *RemoteSysProbeUtil) GetDiscoveryServices() (*discoverymodel.ServicesResponse, error) { - req, err := http.NewRequest(http.MethodGet, discoveryServicesURL, nil) - if err != nil { - return nil, err - } - - resp, err := r.httpClient.Do(req) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("got non-success status code: path %s, url: %s, status_code: %d", r.path, discoveryServicesURL, resp.StatusCode) - } - - res := &discoverymodel.ServicesResponse{} - if err := json.NewDecoder(resp.Body).Decode(res); err != nil { - return nil, err - } - return res, nil -} - func (r *RemoteSysProbeUtil) init() error { resp, err := r.httpClient.Get(statsURL) if err != nil { diff --git a/pkg/process/net/common_linux.go b/pkg/process/net/common_linux.go index f37f6ce5f2f86..3529180ba9576 100644 --- a/pkg/process/net/common_linux.go +++ b/pkg/process/net/common_linux.go @@ -18,15 +18,13 @@ import ( ) const ( - pingURL = "http://unix/" + string(sysconfig.PingModule) + "/ping/" - tracerouteURL = "http://unix/" + string(sysconfig.TracerouteModule) + "/traceroute/" - connectionsURL = "http://unix/" + string(sysconfig.NetworkTracerModule) + "/connections" - networkIDURL = "http://unix/" + string(sysconfig.NetworkTracerModule) + "/network_id" - procStatsURL = "http://unix/" + string(sysconfig.ProcessModule) + "/stats" - registerURL = "http://unix/" + string(sysconfig.NetworkTracerModule) + "/register" - statsURL = "http://unix/debug/stats" - languageDetectionURL = "http://unix/" + string(sysconfig.LanguageDetectionModule) + "/detect" - discoveryServicesURL = "http://unix/" + string(sysconfig.DiscoveryModule) + "/services" + pingURL = "http://unix/" + string(sysconfig.PingModule) + "/ping/" + tracerouteURL = "http://unix/" + string(sysconfig.TracerouteModule) + "/traceroute/" + connectionsURL = "http://unix/" + string(sysconfig.NetworkTracerModule) + "/connections" + networkIDURL = "http://unix/" + string(sysconfig.NetworkTracerModule) + "/network_id" + procStatsURL = "http://unix/" + string(sysconfig.ProcessModule) + "/stats" + registerURL = "http://unix/" + string(sysconfig.NetworkTracerModule) + "/register" + statsURL = "http://unix/debug/stats" ) // CheckPath is used in conjunction with calling the stats endpoint, since we are calling this diff --git a/pkg/process/net/common_unsupported.go b/pkg/process/net/common_unsupported.go index 003c21428832d..c4354fce03fd9 100644 --- a/pkg/process/net/common_unsupported.go +++ b/pkg/process/net/common_unsupported.go @@ -12,8 +12,6 @@ import ( model "github.com/DataDog/agent-payload/v5/process" - discoverymodel "github.com/DataDog/datadog-agent/pkg/collector/corechecks/servicediscovery/model" - "github.com/DataDog/datadog-agent/pkg/languagedetection/languagemodels" nppayload "github.com/DataDog/datadog-agent/pkg/networkpath/payload" ) @@ -63,16 +61,6 @@ func (r *RemoteSysProbeUtil) Register(_ string) error { return ErrNotImplemented } -// DetectLanguage is not supported -func (r *RemoteSysProbeUtil) DetectLanguage([]int32) ([]languagemodels.Language, error) { - return nil, ErrNotImplemented -} - -// GetDiscoveryServices is not supported -func (r *RemoteSysProbeUtil) GetDiscoveryServices() (*discoverymodel.ServicesResponse, error) { - return nil, ErrNotImplemented -} - // GetPing is not supported func (r *RemoteSysProbeUtil) GetPing(_ string, _ string, _ int, _ time.Duration, _ time.Duration) ([]byte, error) { return nil, ErrNotImplemented diff --git a/pkg/process/net/common_windows.go b/pkg/process/net/common_windows.go index befe178b92dba..7a08bca481b93 100644 --- a/pkg/process/net/common_windows.go +++ b/pkg/process/net/common_windows.go @@ -17,15 +17,12 @@ import ( ) const ( - connectionsURL = "http://localhost:3333/" + string(sysconfig.NetworkTracerModule) + "/connections" - networkIDURL = "http://unix/" + string(sysconfig.NetworkTracerModule) + "/network_id" - registerURL = "http://localhost:3333/" + string(sysconfig.NetworkTracerModule) + "/register" - languageDetectionURL = "http://localhost:3333/" + string(sysconfig.LanguageDetectionModule) + "/detect" - statsURL = "http://localhost:3333/debug/stats" - tracerouteURL = "http://localhost:3333/" + string(sysconfig.TracerouteModule) + "/traceroute/" + connectionsURL = "http://localhost:3333/" + string(sysconfig.NetworkTracerModule) + "/connections" + networkIDURL = "http://unix/" + string(sysconfig.NetworkTracerModule) + "/network_id" + registerURL = "http://localhost:3333/" + string(sysconfig.NetworkTracerModule) + "/register" + statsURL = "http://localhost:3333/debug/stats" + tracerouteURL = "http://localhost:3333/" + string(sysconfig.TracerouteModule) + "/traceroute/" - // discovery* is not used on Windows, the value is added to avoid a compilation error - discoveryServicesURL = "http://localhost:3333/" + string(sysconfig.DiscoveryModule) + "/services" // procStatsURL is not used in windows, the value is added to avoid compilation error in windows procStatsURL = "http://localhost:3333/" + string(sysconfig.ProcessModule) + "stats" // pingURL is not used in windows, the value is added to avoid compilation error in windows diff --git a/pkg/process/net/mocks/sys_probe_util.go b/pkg/process/net/mocks/sys_probe_util.go index f5495693f0b32..942046a3bc35d 100644 --- a/pkg/process/net/mocks/sys_probe_util.go +++ b/pkg/process/net/mocks/sys_probe_util.go @@ -3,11 +3,8 @@ package mocks import ( - languagemodels "github.com/DataDog/datadog-agent/pkg/languagedetection/languagemodels" mock "github.com/stretchr/testify/mock" - model "github.com/DataDog/datadog-agent/pkg/collector/corechecks/servicediscovery/model" - payload "github.com/DataDog/datadog-agent/pkg/networkpath/payload" process "github.com/DataDog/agent-payload/v5/process" @@ -28,64 +25,6 @@ func (_m *SysProbeUtil) EXPECT() *SysProbeUtil_Expecter { return &SysProbeUtil_Expecter{mock: &_m.Mock} } -// DetectLanguage provides a mock function with given fields: pids -func (_m *SysProbeUtil) DetectLanguage(pids []int32) ([]languagemodels.Language, error) { - ret := _m.Called(pids) - - if len(ret) == 0 { - panic("no return value specified for DetectLanguage") - } - - var r0 []languagemodels.Language - var r1 error - if rf, ok := ret.Get(0).(func([]int32) ([]languagemodels.Language, error)); ok { - return rf(pids) - } - if rf, ok := ret.Get(0).(func([]int32) []languagemodels.Language); ok { - r0 = rf(pids) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]languagemodels.Language) - } - } - - if rf, ok := ret.Get(1).(func([]int32) error); ok { - r1 = rf(pids) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// SysProbeUtil_DetectLanguage_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DetectLanguage' -type SysProbeUtil_DetectLanguage_Call struct { - *mock.Call -} - -// DetectLanguage is a helper method to define mock.On call -// - pids []int32 -func (_e *SysProbeUtil_Expecter) DetectLanguage(pids interface{}) *SysProbeUtil_DetectLanguage_Call { - return &SysProbeUtil_DetectLanguage_Call{Call: _e.mock.On("DetectLanguage", pids)} -} - -func (_c *SysProbeUtil_DetectLanguage_Call) Run(run func(pids []int32)) *SysProbeUtil_DetectLanguage_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].([]int32)) - }) - return _c -} - -func (_c *SysProbeUtil_DetectLanguage_Call) Return(_a0 []languagemodels.Language, _a1 error) *SysProbeUtil_DetectLanguage_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *SysProbeUtil_DetectLanguage_Call) RunAndReturn(run func([]int32) ([]languagemodels.Language, error)) *SysProbeUtil_DetectLanguage_Call { - _c.Call.Return(run) - return _c -} - // GetConnections provides a mock function with given fields: clientID func (_m *SysProbeUtil) GetConnections(clientID string) (*process.Connections, error) { ret := _m.Called(clientID) @@ -144,63 +83,6 @@ func (_c *SysProbeUtil_GetConnections_Call) RunAndReturn(run func(string) (*proc return _c } -// GetDiscoveryServices provides a mock function with given fields: -func (_m *SysProbeUtil) GetDiscoveryServices() (*model.ServicesResponse, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetDiscoveryServices") - } - - var r0 *model.ServicesResponse - var r1 error - if rf, ok := ret.Get(0).(func() (*model.ServicesResponse, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() *model.ServicesResponse); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*model.ServicesResponse) - } - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// SysProbeUtil_GetDiscoveryServices_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetDiscoveryServices' -type SysProbeUtil_GetDiscoveryServices_Call struct { - *mock.Call -} - -// GetDiscoveryServices is a helper method to define mock.On call -func (_e *SysProbeUtil_Expecter) GetDiscoveryServices() *SysProbeUtil_GetDiscoveryServices_Call { - return &SysProbeUtil_GetDiscoveryServices_Call{Call: _e.mock.On("GetDiscoveryServices")} -} - -func (_c *SysProbeUtil_GetDiscoveryServices_Call) Run(run func()) *SysProbeUtil_GetDiscoveryServices_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *SysProbeUtil_GetDiscoveryServices_Call) Return(_a0 *model.ServicesResponse, _a1 error) *SysProbeUtil_GetDiscoveryServices_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *SysProbeUtil_GetDiscoveryServices_Call) RunAndReturn(run func() (*model.ServicesResponse, error)) *SysProbeUtil_GetDiscoveryServices_Call { - _c.Call.Return(run) - return _c -} - // GetNetworkID provides a mock function with given fields: func (_m *SysProbeUtil) GetNetworkID() (string, error) { ret := _m.Called() diff --git a/pkg/process/net/shared.go b/pkg/process/net/shared.go index 88f5681d7fc3c..33ca74d07863d 100644 --- a/pkg/process/net/shared.go +++ b/pkg/process/net/shared.go @@ -10,8 +10,6 @@ import ( model "github.com/DataDog/agent-payload/v5/process" - discoverymodel "github.com/DataDog/datadog-agent/pkg/collector/corechecks/servicediscovery/model" - "github.com/DataDog/datadog-agent/pkg/languagedetection/languagemodels" nppayload "github.com/DataDog/datadog-agent/pkg/networkpath/payload" ) @@ -25,8 +23,6 @@ type SysProbeUtil interface { GetProcStats(pids []int32) (*model.ProcStatsWithPermByPID, error) Register(clientID string) error GetNetworkID() (string, error) - DetectLanguage(pids []int32) ([]languagemodels.Language, error) - GetDiscoveryServices() (*discoverymodel.ServicesResponse, error) GetPing(clientID string, host string, count int, interval time.Duration, timeout time.Duration) ([]byte, error) GetTraceroute(clientID string, host string, port uint16, protocol nppayload.Protocol, maxTTL uint8, timeout time.Duration) ([]byte, error) }