Skip to content

Commit 8934a6f

Browse files
discovery: load libdd_discovery via CGO bindings
Improves discovery: use dd_discovery_cgo build tag instead of built-in cgo fix omnibus build for CGo library integration Implement some improvements
1 parent b50d30b commit 8934a6f

File tree

11 files changed

+678
-307
lines changed

11 files changed

+678
-307
lines changed

omnibus/config/software/datadog-agent.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,10 @@
186186
copy 'bin/system-probe/system-probe.exe', "#{install_dir}/bin/agent"
187187
else
188188
copy "bin/system-probe/system-probe", "#{install_dir}/embedded/bin"
189+
if linux_target?
190+
mkdir "#{install_dir}/embedded/lib"
191+
copy 'pkg/discovery/module/rust/libdd_discovery.so', "#{install_dir}/embedded/lib"
192+
end
189193
end
190194

191195
# Add SELinux policy for system-probe

pkg/discovery/module/impl_linux.go

Lines changed: 4 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
// This product includes software developed at Datadog (https://www.datadoghq.com/).
44
// Copyright 2024-present Datadog, Inc.
55

6+
//go:build !dd_discovery_cgo
7+
68
package module
79

810
import (
@@ -11,7 +13,6 @@ import (
1113
"errors"
1214
"fmt"
1315
"io"
14-
"net/http"
1516
"os"
1617
"regexp"
1718
"slices"
@@ -30,40 +31,20 @@ import (
3031
"github.com/DataDog/datadog-agent/pkg/discovery/usm"
3132
"github.com/DataDog/datadog-agent/pkg/languagedetection/privileged"
3233
"github.com/DataDog/datadog-agent/pkg/network"
33-
"github.com/DataDog/datadog-agent/pkg/system-probe/api/module"
34+
probemodule "github.com/DataDog/datadog-agent/pkg/system-probe/api/module"
3435
sysconfigtypes "github.com/DataDog/datadog-agent/pkg/system-probe/config/types"
35-
"github.com/DataDog/datadog-agent/pkg/system-probe/utils"
3636
"github.com/DataDog/datadog-agent/pkg/util/kernel"
3737
"github.com/DataDog/datadog-agent/pkg/util/kernel/netns"
3838
"github.com/DataDog/datadog-agent/pkg/util/log"
3939
)
4040

41-
const (
42-
pathServices = "/services"
43-
)
44-
4541
var (
4642
// apmInjectorRegex matches the APM auto-injector launcher.preload.so library path
4743
apmInjectorRegex = regexp.MustCompile(`/opt/datadog-packages/datadog-apm-inject/[^/]+/inject/launcher\.preload\.so`)
4844
)
4945

50-
// Ensure discovery implements the module.Module interface.
51-
var _ module.Module = &discovery{}
52-
53-
// discovery is an implementation of the Module interface for the discovery module.
54-
type discovery struct {
55-
core core.Discovery
56-
57-
config *core.DiscoveryConfig
58-
59-
mux *sync.RWMutex
60-
61-
// privilegedDetector is used to detect the language of a process.
62-
privilegedDetector privileged.LanguageDetector
63-
}
64-
6546
// NewDiscoveryModule creates a new discovery system probe module.
66-
func NewDiscoveryModule(_ *sysconfigtypes.Config, _ module.FactoryDependencies) (module.Module, error) {
47+
func NewDiscoveryModule(_ *sysconfigtypes.Config, _ probemodule.FactoryDependencies) (probemodule.Module, error) {
6748
cfg := core.NewConfig()
6849

6950
d := &discovery{
@@ -78,63 +59,6 @@ func NewDiscoveryModule(_ *sysconfigtypes.Config, _ module.FactoryDependencies)
7859
return d, nil
7960
}
8061

81-
// GetStats returns the stats of the discovery module.
82-
func (s *discovery) GetStats() map[string]any {
83-
return nil
84-
}
85-
86-
// Register registers the discovery module with the provided HTTP mux.
87-
func (s *discovery) Register(httpMux *module.Router) error {
88-
httpMux.HandleFunc("/status", s.handleStatusEndpoint)
89-
httpMux.HandleFunc("/state", s.handleStateEndpoint)
90-
httpMux.HandleFunc(pathServices, utils.WithConcurrencyLimit(utils.DefaultMaxConcurrentRequests, s.handleServices))
91-
92-
return nil
93-
}
94-
95-
// Close cleans resources used by the discovery module.
96-
func (s *discovery) Close() {
97-
s.mux.Lock()
98-
defer s.mux.Unlock()
99-
100-
s.core.Close()
101-
}
102-
103-
// handleStatusEndpoint is the handler for the /status endpoint.
104-
// Reports the status of the discovery module.
105-
func (s *discovery) handleStatusEndpoint(w http.ResponseWriter, _ *http.Request) {
106-
_, _ = w.Write([]byte("Discovery Module is running"))
107-
}
108-
109-
// handleStateEndpoint is the handler for the /state endpoint.
110-
// Returns the internal state of the discovery module.
111-
func (s *discovery) handleStateEndpoint(w http.ResponseWriter, _ *http.Request) {
112-
s.mux.Lock()
113-
defer s.mux.Unlock()
114-
115-
state := make(map[string]interface{})
116-
117-
utils.WriteAsJSON(w, state, utils.CompactOutput)
118-
}
119-
120-
func (s *discovery) handleServices(w http.ResponseWriter, req *http.Request) {
121-
params, err := core.ParseParamsFromRequest(req)
122-
if err != nil {
123-
_ = log.Errorf("invalid params to /discovery%s: %v", pathServices, err)
124-
w.WriteHeader(http.StatusBadRequest)
125-
return
126-
}
127-
128-
services, err := s.getServices(params)
129-
if err != nil {
130-
_ = log.Errorf("failed to handle /discovery%s: %v", pathServices, err)
131-
w.WriteHeader(http.StatusInternalServerError)
132-
return
133-
}
134-
135-
utils.WriteAsJSON(w, services, utils.CompactOutput)
136-
}
137-
13862
// socketInfo stores information related to each socket.
13963
type socketInfo struct {
14064
port uint16

pkg/discovery/module/impl_linux_test.go

Lines changed: 1 addition & 212 deletions
Original file line numberDiff line numberDiff line change
@@ -5,224 +5,28 @@
55

66
// This doesn't need BPF, but it's built with this tag to only run with
77
// system-probe tests.
8-
//go:build test && linux_bpf
8+
//go:build test && linux_bpf && !dd_discovery_cgo
99

1010
package module
1111

1212
import (
13-
"bytes"
14-
"context"
15-
"encoding/json"
1613
"net"
17-
"net/http"
18-
"net/http/httptest"
1914
"os"
2015
"os/exec"
2116
"path/filepath"
2217
"strconv"
2318
"strings"
24-
"syscall"
2519
"testing"
2620

27-
gorillamux "github.com/gorilla/mux"
2821
"github.com/prometheus/procfs"
29-
"github.com/shirou/gopsutil/v4/process"
3022
"github.com/stretchr/testify/require"
31-
"golang.org/x/sys/unix"
3223

33-
"github.com/DataDog/datadog-agent/pkg/discovery/core"
3424
"github.com/DataDog/datadog-agent/pkg/discovery/language"
35-
"github.com/DataDog/datadog-agent/pkg/discovery/model"
3625
"github.com/DataDog/datadog-agent/pkg/network"
3726
"github.com/DataDog/datadog-agent/pkg/network/protocols/http/testutil"
38-
usmtestutil "github.com/DataDog/datadog-agent/pkg/network/usm/testutil"
39-
"github.com/DataDog/datadog-agent/pkg/system-probe/api/module"
40-
"github.com/DataDog/datadog-agent/pkg/system-probe/config"
4127
"github.com/DataDog/datadog-agent/pkg/util/kernel"
4228
)
4329

44-
func findService(pid int, services []model.Service) *model.Service {
45-
for _, s := range services {
46-
if s.PID == pid {
47-
return &s
48-
}
49-
}
50-
51-
return nil
52-
}
53-
54-
type testDiscoveryModule struct {
55-
url string
56-
}
57-
58-
func setupDiscoveryModule(t *testing.T) *testDiscoveryModule {
59-
t.Helper()
60-
mux := gorillamux.NewRouter()
61-
62-
mod, err := NewDiscoveryModule(nil, module.FactoryDependencies{})
63-
require.NoError(t, err)
64-
discovery := mod.(*discovery)
65-
66-
discovery.Register(module.NewRouter(string(config.DiscoveryModule), mux))
67-
t.Cleanup(discovery.Close)
68-
69-
srv := httptest.NewServer(mux)
70-
t.Cleanup(srv.Close)
71-
72-
return &testDiscoveryModule{
73-
url: srv.URL,
74-
}
75-
}
76-
77-
// makeRequest wraps the request to the discovery module, setting the JSON body if provided,
78-
// and returning the response as the given type.
79-
func makeRequest[T any](t require.TestingT, url string, params *core.Params) *T {
80-
var body *bytes.Buffer
81-
if params != nil {
82-
jsonData, err := params.ToJSON()
83-
require.NoError(t, err, "failed to serialize params to JSON")
84-
body = bytes.NewBuffer(jsonData)
85-
}
86-
87-
var req *http.Request
88-
var err error
89-
if body != nil {
90-
req, err = http.NewRequest(http.MethodPost, url, body)
91-
req.Header.Set("Content-Type", "application/json")
92-
} else {
93-
req, err = http.NewRequest(http.MethodPost, url, nil)
94-
}
95-
require.NoError(t, err, "failed to create request")
96-
97-
resp, err := http.DefaultClient.Do(req)
98-
require.NoError(t, err, "failed to send request")
99-
defer resp.Body.Close()
100-
101-
res := new(T)
102-
err = json.NewDecoder(resp.Body).Decode(res)
103-
require.NoError(t, err, "failed to decode response")
104-
105-
return res
106-
}
107-
108-
// getRunningPids wraps the process.Pids function, returning a slice of ints
109-
// that can be used as the pids query param.
110-
func getRunningPids(t require.TestingT) []int32 {
111-
pids, err := process.Pids()
112-
require.NoError(t, err)
113-
return pids
114-
}
115-
116-
func startTCPServer(t *testing.T, proto string, address string) (*os.File, *net.TCPAddr) {
117-
listener, err := net.Listen(proto, address)
118-
require.NoError(t, err)
119-
t.Cleanup(func() { _ = listener.Close() })
120-
tcpAddr := listener.Addr().(*net.TCPAddr)
121-
122-
f, err := listener.(*net.TCPListener).File()
123-
defer listener.Close()
124-
require.NoError(t, err)
125-
126-
return f, tcpAddr
127-
}
128-
129-
func startTCPClient(t *testing.T, proto string, server *net.TCPAddr) (*os.File, *net.TCPAddr) {
130-
client, err := net.DialTCP(proto, nil, server)
131-
require.NoError(t, err)
132-
t.Cleanup(func() { _ = client.Close() })
133-
134-
f, err := client.File()
135-
defer client.Close()
136-
require.NoError(t, err)
137-
138-
return f, client.LocalAddr().(*net.TCPAddr)
139-
}
140-
141-
func startUDPServer(t *testing.T, proto string, address string) (*os.File, *net.UDPAddr) {
142-
lnPacket, err := net.ListenPacket(proto, address)
143-
require.NoError(t, err)
144-
t.Cleanup(func() { _ = lnPacket.Close() })
145-
146-
f, err := lnPacket.(*net.UDPConn).File()
147-
defer lnPacket.Close()
148-
require.NoError(t, err)
149-
150-
return f, lnPacket.LocalAddr().(*net.UDPAddr)
151-
}
152-
153-
func startUDPClient(t *testing.T, proto string, server *net.UDPAddr) (*os.File, *net.UDPAddr) {
154-
udpClient, err := net.DialUDP(proto, nil, server)
155-
require.NoError(t, err)
156-
t.Cleanup(func() { _ = udpClient.Close() })
157-
158-
f, err := udpClient.File()
159-
defer udpClient.Close()
160-
require.NoError(t, err)
161-
162-
return f, udpClient.LocalAddr().(*net.UDPAddr)
163-
}
164-
165-
func disableCloseOnExec(t *testing.T, f *os.File) {
166-
_, _, syserr := syscall.Syscall(syscall.SYS_FCNTL, f.Fd(), syscall.F_SETFD, 0)
167-
require.Equal(t, syscall.Errno(0x0), syserr)
168-
}
169-
170-
func startProcessWithFile(t *testing.T, f *os.File) *exec.Cmd {
171-
ctx, cancel := context.WithCancel(context.Background())
172-
173-
// Disable close-on-exec so that the process gets it
174-
t.Cleanup(func() { f.Close() })
175-
disableCloseOnExec(t, f)
176-
177-
cmd := exec.CommandContext(ctx, "sleep", "1000")
178-
t.Cleanup(func() {
179-
cancel()
180-
_ = cmd.Wait()
181-
})
182-
err := cmd.Start()
183-
require.NoError(t, err)
184-
f.Close()
185-
186-
return cmd
187-
}
188-
189-
func makeAlias(t *testing.T, alias string, serverBin string) string {
190-
binDir := filepath.Dir(serverBin)
191-
aliasPath := filepath.Join(binDir, alias)
192-
193-
target, err := os.Readlink(aliasPath)
194-
if err == nil && target == serverBin {
195-
return aliasPath
196-
}
197-
198-
os.Remove(aliasPath)
199-
err = os.Symlink(serverBin, aliasPath)
200-
require.NoError(t, err)
201-
202-
return aliasPath
203-
}
204-
205-
func buildFakeServer(t *testing.T) string {
206-
curDir, err := testutil.CurDir()
207-
require.NoError(t, err)
208-
serverBin, err := usmtestutil.BuildGoBinaryWrapper(filepath.Join(curDir, "testutil"), "fake_server")
209-
require.NoError(t, err)
210-
211-
for _, alias := range []string{"java", "node", "sshd", "dotnet"} {
212-
makeAlias(t, alias, serverBin)
213-
}
214-
215-
return filepath.Dir(serverBin)
216-
}
217-
218-
func newDiscovery() *discovery {
219-
mod, err := NewDiscoveryModule(nil, module.FactoryDependencies{})
220-
if err != nil {
221-
panic(err)
222-
}
223-
return mod.(*discovery)
224-
}
225-
22630
// addSockets adds only listening sockets to a map to be used for later looksups.
22731
func addSockets[P procfs.NetTCP | procfs.NetUDP](sockMap map[uint64]socketInfo, sockets P,
22832
family network.ConnectionFamily, ctype network.ConnectionType, state uint64,
@@ -315,21 +119,6 @@ func BenchmarkGetNSInfoOld(b *testing.B) {
315119
}
316120
}
317121

318-
func createTracerMemfd(t *testing.T, data []byte) int {
319-
t.Helper()
320-
fd, err := unix.MemfdCreate("datadog-tracer-info-xxx", 0)
321-
require.NoError(t, err)
322-
t.Cleanup(func() { unix.Close(fd) })
323-
err = unix.Ftruncate(fd, int64(len(data)))
324-
require.NoError(t, err)
325-
mappedData, err := unix.Mmap(fd, 0, len(data), unix.PROT_READ|unix.PROT_WRITE, unix.MAP_SHARED)
326-
require.NoError(t, err)
327-
copy(mappedData, data)
328-
err = unix.Munmap(mappedData)
329-
require.NoError(t, err)
330-
return fd
331-
}
332-
333122
func TestValidInvalidTracerMetadata(t *testing.T) {
334123
discovery := newDiscovery()
335124
require.NotEmpty(t, discovery)

0 commit comments

Comments
 (0)