Skip to content

Commit 7a5e80d

Browse files
sklgrcevski
andauthored
test: integration test drift and sync (#2241)
* test: integration test drift and sync * test: sync obi integration tests * test: more jsonrpc changes needed * fix: Add missing JSRONRPC_PORT to duplicate_testserver.sh * chore: backport 779 open-telemetry/opentelemetry-ebpf-instrumentation#779 * add missing port * fix wrapper * fix: trailing YAML separator causes duplicate manifest * fix: exclude beyla container from survey discovery * Revert "fix: exclude beyla container from survey discovery" This reverts commit 69dbf1d. * feat: container name default exclusion --------- Co-authored-by: Nikola Grcevski <[email protected]>
1 parent 71fe928 commit 7a5e80d

28 files changed

+721
-149
lines changed

pkg/beyla/config_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,9 @@ network:
273273
services.GlobAttributes{
274274
Metadata: map[string]*services.GlobAttr{"k8s_namespace": &servicesextra.K8sDefaultNamespacesGlob},
275275
},
276+
services.GlobAttributes{
277+
Metadata: map[string]*services.GlobAttr{"k8s_container_name": &servicesextra.K8sDefaultExcludeContainerNamesGlob},
278+
},
276279
},
277280
DefaultOtlpGRPCPort: 4317,
278281
RouteHarvesterTimeout: 10 * time.Second,

pkg/services/criteria.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ var K8sDefaultNamespacesGlob = services.NewGlob("{kube-system,kube-node-lease,lo
2222

2323
var K8sDefaultNamespacesWithSurveyRegex = services.NewRegexp("^kube-system$|^kube-node-lease$|^local-path-storage$|^cert-manager$" + k8sGKEDefaultNamespacesRegex + k8sAKSDefaultNamespacesRegex)
2424
var K8sDefaultNamespacesWithSurveyGlob = services.NewGlob("{kube-system,kube-node-lease,local-path-storage,cert-manager" + k8sGKEDefaultNamespacesGlob + k8sAKSDefaultNamespacesGlob + "}")
25+
var K8sDefaultExcludeContainerNamesGlob = services.NewGlob("{beyla,ebpf-instrument,alloy,prometheus-config-reloader,otelcol,otelcol-contrib}")
2526

2627
var DefaultExcludeServices = services.RegexDefinitionCriteria{
2728
services.RegexSelector{
@@ -47,6 +48,9 @@ var DefaultExcludeInstrument = services.GlobDefinitionCriteria{
4748
services.GlobAttributes{
4849
Metadata: map[string]*services.GlobAttr{"k8s_namespace": &K8sDefaultNamespacesGlob},
4950
},
51+
services.GlobAttributes{
52+
Metadata: map[string]*services.GlobAttr{"k8s_container_name": &K8sDefaultExcludeContainerNamesGlob},
53+
},
5054
}
5155
var DefaultExcludeInstrumentWithSurvey = services.GlobDefinitionCriteria{
5256
services.GlobAttributes{
@@ -55,6 +59,9 @@ var DefaultExcludeInstrumentWithSurvey = services.GlobDefinitionCriteria{
5559
services.GlobAttributes{
5660
Metadata: map[string]*services.GlobAttr{"k8s_namespace": &K8sDefaultNamespacesWithSurveyGlob},
5761
},
62+
services.GlobAttributes{
63+
Metadata: map[string]*services.GlobAttr{"k8s_container_name": &K8sDefaultExcludeContainerNamesGlob},
64+
},
5865
}
5966

6067
// DiscoveryConfig for the discover.ProcessFinder pipeline

scripts/check-obi-drift.sh

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
#!/usr/bin/env bash
2+
# Detect and optionally sync test functions that have drifted between Beyla and OBI
3+
#
4+
# Usage:
5+
# ./scripts/check-obi-drift.sh # Check for drift (exit 1 if found)
6+
# ./scripts/check-obi-drift.sh --sync # Apply OBI changes to Beyla files
7+
# ./scripts/check-obi-drift.sh --help # Show help
8+
9+
set -euo pipefail
10+
11+
OBI_DIR=".obi-src/test/integration"
12+
BEYLA_DIR="test/integration"
13+
SYNC_MODE=false
14+
15+
# Find test functions that exist in both Beyla and OBI
16+
find_common_functions() {
17+
# Get function names from both directories
18+
local beyla_funcs=$(grep -rh "^func test[a-zA-Z0-9_]*(" "$BEYLA_DIR"/*.go 2>/dev/null | \
19+
sed 's/^func \([a-zA-Z0-9_]*\).*/\1/' | sort -u)
20+
21+
local obi_funcs=$(grep -rh "^func test[a-zA-Z0-9_]*(" "$OBI_DIR"/*.go 2>/dev/null | \
22+
sed 's/^func \([a-zA-Z0-9_]*\).*/\1/' | sort -u)
23+
24+
# Find common functions
25+
comm -12 <(echo "$beyla_funcs") <(echo "$obi_funcs")
26+
}
27+
28+
# Extract function body from a file
29+
extract_function() {
30+
local func="$1"
31+
local file="$2"
32+
33+
# Extract function from start to closing brace at column 0
34+
# Use \( to match the opening parenthesis and avoid matching function name prefixes
35+
sed -n "/^func $func(/,/^}/p" "$file"
36+
}
37+
38+
# Find which file contains a function in a directory
39+
find_function_file() {
40+
local func="$1"
41+
local dir="$2"
42+
43+
grep -l "^func $func(" "$dir"/*.go 2>/dev/null | head -1
44+
}
45+
46+
# Sync function from OBI to Beyla
47+
sync_function() {
48+
local func="$1"
49+
local beyla_file="$2"
50+
local obi_file="$3"
51+
52+
echo " Syncing $func from OBI..."
53+
54+
# Create temp file with transformed function
55+
local temp_func=$(mktemp)
56+
57+
# Extract the OBI function body and transform for Beyla
58+
extract_function "$func" "$obi_file" | \
59+
sed 's|go.opentelemetry.io/obi|github.com/grafana/beyla|g' | \
60+
sed 's|obi_|beyla_|g' | \
61+
sed 's|service_name="opentelemetry-ebpf-instrumentation"|service_name="beyla"|g' | \
62+
sed 's|telemetry.sdk.name", Type: "string", Value: "opentelemetry-ebpf-instrumentation"|telemetry.sdk.name", Type: "string", Value: "beyla"|g' \
63+
> "$temp_func"
64+
65+
# Use go run with flags (avoids *_test.go file path parsing issues)
66+
local script_dir="$(dirname "$0")"
67+
go run "$script_dir/replace-function.go" -file "$beyla_file" -func "$func" -new "$temp_func"
68+
69+
# Clean up
70+
rm -f "$temp_func"
71+
72+
echo " ✓ Synced $func in ${beyla_file##*/}"
73+
}
74+
75+
# Compare function implementations
76+
check_drift() {
77+
local func="$1"
78+
79+
# Find files containing this function
80+
local beyla_file=$(find_function_file "$func" "$BEYLA_DIR")
81+
local obi_file=$(find_function_file "$func" "$OBI_DIR")
82+
83+
if [[ -z "$beyla_file" ]] || [[ -z "$obi_file" ]]; then
84+
return 0 # Skip if not found in both
85+
fi
86+
87+
# Extract function bodies
88+
local beyla_body=$(extract_function "$func" "$beyla_file")
89+
local obi_body=$(extract_function "$func" "$obi_file")
90+
91+
# Normalize to ignore copyright, import path, and metric name changes
92+
local beyla_normalized=$(echo "$beyla_body" | \
93+
grep -v "Copyright The OpenTelemetry Authors" | \
94+
grep -v "SPDX-License-Identifier: Apache-2.0" | \
95+
sed 's|github.com/grafana/beyla|go.opentelemetry.io/obi|g' | \
96+
sed 's|beyla_|obi_|g' | \
97+
sed 's|service_name="beyla"|service_name="opentelemetry-ebpf-instrumentation"|g' | \
98+
sed 's|telemetry.sdk.name", Type: "string", Value: "beyla"|telemetry.sdk.name", Type: "string", Value: "opentelemetry-ebpf-instrumentation"|g')
99+
local obi_normalized=$(echo "$obi_body" | \
100+
grep -v "Copyright The OpenTelemetry Authors" | \
101+
grep -v "SPDX-License-Identifier: Apache-2.0")
102+
103+
if [[ "$beyla_normalized" != "$obi_normalized" ]]; then
104+
echo "$func"
105+
echo " Beyla: ${beyla_file##*/}"
106+
echo " OBI: ${obi_file##*/}"
107+
echo ""
108+
109+
if [[ "$SYNC_MODE" == "true" ]]; then
110+
sync_function "$func" "$beyla_file" "$obi_file"
111+
else
112+
# Show color-coded diff using normalized versions
113+
diff -u \
114+
<(echo "$beyla_normalized") \
115+
<(echo "$obi_normalized") 2>/dev/null | \
116+
sed 's/^-/\x1b[31m-/; s/^+/\x1b[32m+/; s/^@/\x1b[36m@/; s/$/\x1b[0m/' || true
117+
fi
118+
119+
echo ""
120+
return 1
121+
fi
122+
123+
return 0
124+
}
125+
126+
# Show help
127+
show_help() {
128+
cat << EOF
129+
Usage: $0 [OPTIONS]
130+
131+
Check for drift between Beyla and OBI test functions, and optionally sync them.
132+
133+
Options:
134+
--sync Apply OBI changes to Beyla files
135+
--help Show this help message
136+
137+
Examples:
138+
# Check for drift (exit 1 if any found)
139+
$0
140+
141+
# Apply OBI changes to Beyla files
142+
$0 --sync
143+
144+
When --sync is used:
145+
- Replaces drifted Beyla functions with OBI versions
146+
- Creates a git-ready changeset
147+
- You can review changes with 'git diff' and create a PR
148+
149+
EOF
150+
}
151+
152+
# Main
153+
main() {
154+
if [[ "$SYNC_MODE" == "true" ]]; then
155+
echo "Syncing drifted test functions from OBI to Beyla..."
156+
else
157+
echo "Checking for drift between Beyla and OBI test functions..."
158+
fi
159+
echo ""
160+
161+
local common_funcs=$(find_common_functions)
162+
local total=0
163+
local drifted=0
164+
165+
for func in $common_funcs; do
166+
((total++))
167+
if ! check_drift "$func"; then
168+
((drifted++))
169+
fi
170+
done
171+
172+
echo "Summary: $drifted/$total test functions have drifted from OBI"
173+
174+
if [[ $drifted -gt 0 ]]; then
175+
echo ""
176+
if [[ "$SYNC_MODE" == "true" ]]; then
177+
echo "✓ Synced $drifted functions from OBI"
178+
echo ""
179+
echo "Next steps:"
180+
echo " 1. Review changes: git diff test/integration/"
181+
echo " 2. Run tests: go test -tags=integration ./test/integration/..."
182+
echo " 3. Create PR: git add test/integration/ && git commit -m 'Sync tests from OBI'"
183+
else
184+
echo "To sync these changes automatically:"
185+
echo " $0 --sync"
186+
echo ""
187+
echo "To see full diff for a function:"
188+
echo " diff -u test/integration/traces_test.go .obi-src/test/integration/traces_test.go"
189+
exit 1
190+
fi
191+
fi
192+
}
193+
194+
# Parse arguments
195+
case "${1:-}" in
196+
--sync)
197+
SYNC_MODE=true
198+
;;
199+
--help)
200+
show_help
201+
exit 0
202+
;;
203+
"")
204+
# Default: check mode
205+
;;
206+
*)
207+
echo "Error: Unknown option: $1"
208+
echo ""
209+
show_help
210+
exit 1
211+
;;
212+
esac
213+
214+
main
215+

scripts/replace-function.go

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"flag"
6+
"fmt"
7+
"os"
8+
"regexp"
9+
)
10+
11+
// findFunction finds the start and end byte positions of a function in source code
12+
func findFunction(content []byte, funcName string) (start, end int, err error) {
13+
// Find function declaration
14+
funcPattern := fmt.Sprintf(`(?m)^func %s\(`, regexp.QuoteMeta(funcName))
15+
re := regexp.MustCompile(funcPattern)
16+
17+
loc := re.FindIndex(content)
18+
if loc == nil {
19+
return 0, 0, fmt.Errorf("function %s not found", funcName)
20+
}
21+
22+
start = loc[0]
23+
24+
// Find the opening brace
25+
openBrace := -1
26+
for i := loc[1]; i < len(content); i++ {
27+
if content[i] == '{' {
28+
openBrace = i
29+
break
30+
}
31+
}
32+
33+
if openBrace == -1 {
34+
return 0, 0, fmt.Errorf("opening brace not found for function %s", funcName)
35+
}
36+
37+
// Count braces to find matching closing brace
38+
braceCount := 1
39+
for i := openBrace + 1; i < len(content); i++ {
40+
switch content[i] {
41+
case '{':
42+
braceCount++
43+
case '}':
44+
braceCount--
45+
if braceCount == 0 {
46+
// Found the matching closing brace
47+
// Include the closing brace and newline if present
48+
end = i + 1
49+
if end < len(content) && content[end] == '\n' {
50+
end++
51+
}
52+
return start, end, nil
53+
}
54+
}
55+
}
56+
57+
return 0, 0, fmt.Errorf("matching closing brace not found for function %s", funcName)
58+
}
59+
60+
// replaceFunction replaces a function in a Go file with new content, preserving exact formatting
61+
func replaceFunction(filepath, funcName, newFuncContent string) error {
62+
// Read the file
63+
content, err := os.ReadFile(filepath)
64+
if err != nil {
65+
return fmt.Errorf("failed to read file: %w", err)
66+
}
67+
68+
// Find the function to replace
69+
start, end, err := findFunction(content, funcName)
70+
if err != nil {
71+
return err
72+
}
73+
74+
// Build new content: before + new function + after
75+
var buf bytes.Buffer
76+
buf.Write(content[:start])
77+
buf.WriteString(newFuncContent)
78+
if end < len(content) {
79+
buf.Write(content[end:])
80+
}
81+
82+
// Write back to file
83+
return os.WriteFile(filepath, buf.Bytes(), 0644)
84+
}
85+
86+
func main() {
87+
var (
88+
filepath string
89+
funcName string
90+
newFuncFile string
91+
)
92+
93+
flag := flag.NewFlagSet(os.Args[0], flag.ExitOnError)
94+
flag.StringVar(&filepath, "file", "", "Go source file to modify")
95+
flag.StringVar(&funcName, "func", "", "Function name to replace")
96+
flag.StringVar(&newFuncFile, "new", "", "File containing new function content")
97+
98+
if err := flag.Parse(os.Args[1:]); err != nil {
99+
fmt.Fprintf(os.Stderr, "Error parsing flags: %v\n", err)
100+
os.Exit(1)
101+
}
102+
103+
if filepath == "" || funcName == "" || newFuncFile == "" {
104+
fmt.Fprintf(os.Stderr, "Usage: %s -file <file> -func <function_name> -new <new_function_file>\n", os.Args[0])
105+
flag.PrintDefaults()
106+
os.Exit(1)
107+
}
108+
109+
// Read new function content
110+
newFuncContent, err := os.ReadFile(newFuncFile)
111+
if err != nil {
112+
fmt.Fprintf(os.Stderr, "Error reading new function file: %v\n", err)
113+
os.Exit(1)
114+
}
115+
116+
if err := replaceFunction(filepath, funcName, string(newFuncContent)); err != nil {
117+
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
118+
os.Exit(1)
119+
}
120+
121+
fmt.Printf("✓ Replaced %s in %s\n", funcName, filepath)
122+
}

0 commit comments

Comments
 (0)