Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Backport 7.64.x] fix(autoinstrumentation): enable language-detection when pinned versions are defaults #34506

Merged
merged 1 commit into from
Feb 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -452,46 +452,25 @@ func TestInjectAutoInstruConfigV2(t *testing.T) {
}

func TestExtractLibInfo(t *testing.T) {
defaultLibImageVersions := map[string]string{
"java": "registry/dd-lib-java-init:v1",
"js": "registry/dd-lib-js-init:v5",
"python": "registry/dd-lib-python-init:v2",
"dotnet": "registry/dd-lib-dotnet-init:v3",
"ruby": "registry/dd-lib-ruby-init:v2",
}

// TODO: Add new entry when a new language is supported
allLatestDefaultLibs := []libInfo{
{
lang: "java",
image: defaultLibImageVersions["java"],
},
{
lang: "js",
image: defaultLibImageVersions["js"],
},
{
lang: "python",
image: defaultLibImageVersions["python"],
},
{
lang: "dotnet",
image: defaultLibImageVersions["dotnet"],
},
{
lang: "ruby",
image: defaultLibImageVersions["ruby"],
},
defaultLibInfo(java),
defaultLibInfo(js),
defaultLibInfo(python),
defaultLibInfo(dotnet),
defaultLibInfo(ruby),
}

var mockConfig model.Config
tests := []struct {
name string
pod *corev1.Pod
containerRegistry string
expectedLibsToInject []libInfo
expectedPodEligible *bool
setupConfig func()
name string
pod *corev1.Pod
deployments []common.MockDeployment
assertExtractedLibInfo func(*testing.T, extractedPodLibInfo)
containerRegistry string
expectedLibsToInject []libInfo
expectedPodEligible *bool
setupConfig func()
}{
{
name: "java",
Expand Down Expand Up @@ -785,10 +764,7 @@ func TestExtractLibInfo(t *testing.T) {
pod: common.FakePodWithNamespaceAndLabel("ns", "", ""),
containerRegistry: "registry",
expectedLibsToInject: []libInfo{
{
lang: "java",
image: defaultLibImageVersions["java"],
},
defaultLibInfo(java),
},
setupConfig: func() {
mockConfig.SetWithoutSource("apm_config.instrumentation.enabled", true)
Expand Down Expand Up @@ -830,6 +806,74 @@ func TestExtractLibInfo(t *testing.T) {
mockConfig.SetWithoutSource("admission_controller.mutate_unlabelled", true)
},
},
{
name: "pod with lang-detection deployment and default libs",
pod: common.FakePodSpec{
ParentKind: "replicaset",
ParentName: "deployment-123",
}.Create(),
deployments: []common.MockDeployment{
{
ContainerName: "pod",
DeploymentName: "deployment",
Namespace: "ns",
Languages: languageSetOf("python"),
},
},
containerRegistry: "registry",
assertExtractedLibInfo: func(t *testing.T, i extractedPodLibInfo) {
t.Helper()
require.Equal(t, &libInfoLanguageDetection{
libs: []libInfo{
python.defaultLibInfo("registry", "pod"),
},
injectionEnabled: true,
}, i.languageDetection)
},
expectedLibsToInject: []libInfo{
python.defaultLibInfo("registry", "pod"),
},
setupConfig: func() {
mockConfig.SetWithoutSource("apm_config.instrumentation.enabled", true)
mockConfig.SetWithoutSource("apm_config.instrumentation.lib_versions", defaultLibraries)
mockConfig.SetWithoutSource("language_detection.enabled", true)
mockConfig.SetWithoutSource("language_detection.reporting.enabled", true)
mockConfig.SetWithoutSource("admission_controller.auto_instrumentation.inject_auto_detected_libraries", true)
},
},
{
name: "pod with lang-detection deployment and libs set",
pod: common.FakePodSpec{
ParentKind: "replicaset",
ParentName: "deployment-123",
}.Create(),
deployments: []common.MockDeployment{
{
ContainerName: "pod",
DeploymentName: "deployment",
Namespace: "ns",
Languages: languageSetOf("python"),
},
},
containerRegistry: "registry",
assertExtractedLibInfo: func(t *testing.T, i extractedPodLibInfo) {
t.Helper()
require.Equal(t, &libInfoLanguageDetection{
libs: []libInfo{python.defaultLibInfo("registry", "pod")},
injectionEnabled: true,
}, i.languageDetection)
},
expectedLibsToInject: []libInfo{
java.defaultLibInfo("registry", ""),
},
setupConfig: func() {
mockConfig.SetWithoutSource("apm_config.instrumentation.enabled", true)
mockConfig.SetWithoutSource("apm_config.instrumentation.lib_versions", defaultLibrariesFor("java"))
mockConfig.SetWithoutSource("language_detection.enabled", true)
mockConfig.SetWithoutSource("language_detection.reporting.enabled", true)
mockConfig.SetWithoutSource("admission_controller.auto_instrumentation.inject_auto_detected_libraries", true)
},
},
{
name: "php (opt-in)",
pod: common.FakePodWithAnnotation("admission.datadoghq.com/php-lib.version", "v1"),
Expand All @@ -856,10 +900,7 @@ func TestExtractLibInfo(t *testing.T) {
mockConfig.SetWithoutSource(k, v)
}

wmeta := fxutil.Test[workloadmeta.Component](t,
core.MockBundle(),
workloadmetafxmock.MockModule(workloadmeta.NewParams()),
)
wmeta := common.FakeStoreWithDeployment(t, tt.deployments)
mockConfig = configmock.New(t)
for k, v := range overrides {
mockConfig.SetWithoutSource(k, v)
Expand All @@ -879,6 +920,9 @@ func TestExtractLibInfo(t *testing.T) {
}

extracted := mutator.extractLibInfo(tt.pod)
if tt.assertExtractedLibInfo != nil {
tt.assertExtractedLibInfo(t, extracted)
}
require.ElementsMatch(t, tt.expectedLibsToInject, extracted.libs)
})
}
Expand Down
31 changes: 24 additions & 7 deletions pkg/clusteragent/admission/mutate/autoinstrumentation/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -346,27 +346,44 @@ func getOptionalStringValue(datadogConfig config.Component, key string) *string
return value
}

type pinnedLibraries struct {
// libs are the pinned libraries themselves.
libs []libInfo
// areSetToDefaults is true when the libs coming from the configuration
// are equivalent to what would be set if there was no configuration at all.
areSetToDefaults bool
}

// getPinnedLibraries returns tracing libraries to inject as configured by apm_config.instrumentation.lib_versions
// given a registry.
func getPinnedLibraries(libVersions map[string]string, registry string) []libInfo {
var res []libInfo
func getPinnedLibraries(libVersions map[string]string, registry string, checkDefaults bool) pinnedLibraries {
libs := []libInfo{}
allDefaults := true

for lang, version := range libVersions {
l := language(lang)
if !l.isSupported() {
log.Warnf("APM Instrumentation detected configuration for unsupported language: %s. Tracing library for %s will not be injected", lang, lang)
continue
}

log.Infof("Library version %s is specified for language %s", version, lang)
res = append(res, l.libInfo("", l.libImageName(registry, version)))
info := l.libInfo("", l.libImageName(registry, version))
log.Infof("Library version %s is specified for language %s, going to use %s", version, lang, info.image)
libs = append(libs, info)

if info.image != l.libImageName(registry, l.defaultLibVersion()) {
allDefaults = false
}
}

return res
return pinnedLibraries{
libs: libs,
areSetToDefaults: checkDefaults && allDefaults && len(libs) == len(defaultSupportedLanguagesMap()),
}
}

func initDefaultResources(datadogConfig config.Component) (initResourceRequirementConfiguration, error) {

var conf = initResourceRequirementConfiguration{}
conf := initResourceRequirementConfiguration{}

if datadogConfig.IsSet("admission_controller.auto_instrumentation.init_resources.cpu") {
quantity, err := resource.ParseQuantity(datadogConfig.GetString("admission_controller.auto_instrumentation.init_resources.cpu"))
Expand Down
131 changes: 131 additions & 0 deletions pkg/clusteragent/admission/mutate/autoinstrumentation/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -259,3 +259,134 @@ func TestTargetEnvVar(t *testing.T) {
})
}
}

func TestGetPinnedLibraries(t *testing.T) {
tests := []struct {
name string
libVersions map[string]string
checkDefaults bool
expected pinnedLibraries
}{
{
name: "no pinned library versions",
expected: pinnedLibraries{areSetToDefaults: false},
},
{
name: "no pinned library versions, checkDefaults",
checkDefaults: true,
expected: pinnedLibraries{areSetToDefaults: false},
},
{
name: "default libs, not checking defaults always false",
libVersions: defaultLibraries,
expected: pinnedLibraries{
libs: []libInfo{
defaultLibInfo(java),
defaultLibInfo(python),
defaultLibInfo(js),
defaultLibInfo(dotnet),
defaultLibInfo(ruby),
},
},
},
{
name: "default libs",
libVersions: defaultLibraries,
checkDefaults: true,
expected: pinnedLibraries{
libs: []libInfo{
defaultLibInfo(java),
defaultLibInfo(python),
defaultLibInfo(js),
defaultLibInfo(dotnet),
defaultLibInfo(ruby),
},
areSetToDefaults: true,
},
},
{
name: "default libs, one missing",
libVersions: defaultLibrariesFor("java", "python", "js", "dotnet"),
checkDefaults: true,
expected: pinnedLibraries{
libs: []libInfo{
defaultLibInfo(java),
defaultLibInfo(python),
defaultLibInfo(js),
defaultLibInfo(dotnet),
},
},
},
{
name: "explicitly default libs",
libVersions: map[string]string{
"java": "default",
"python": "default",
"js": "default",
"dotnet": "default",
"ruby": "default",
},
checkDefaults: true,
expected: pinnedLibraries{
libs: []libInfo{
defaultLibInfo(java),
defaultLibInfo(python),
defaultLibInfo(js),
defaultLibInfo(dotnet),
defaultLibInfo(ruby),
},
areSetToDefaults: true,
},
},
{
name: "default libs (major versions)",
libVersions: map[string]string{
"java": "v1",
"python": "v2",
"js": "v5",
"dotnet": "v3",
"ruby": "v2",
},
checkDefaults: true,
expected: pinnedLibraries{
libs: []libInfo{
defaultLibInfo(java),
defaultLibInfo(python),
defaultLibInfo(js),
defaultLibInfo(dotnet),
defaultLibInfo(ruby),
},
areSetToDefaults: true,
},
},
{
name: "default libs (major versions mismatch)",
libVersions: map[string]string{
"java": "v1",
"python": "v2",
"js": "v3",
"dotnet": "v3",
"ruby": "v2",
},
checkDefaults: true,
expected: pinnedLibraries{
libs: []libInfo{
defaultLibInfo(java),
defaultLibInfo(python),
js.libInfo("", "registry/dd-lib-js-init:v3"),
defaultLibInfo(dotnet),
defaultLibInfo(ruby),
},
areSetToDefaults: false,
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
pinned := getPinnedLibraries(tt.libVersions, "registry", tt.checkDefaults)
require.ElementsMatch(t, tt.expected.libs, pinned.libs, "libs match")
require.Equal(t, tt.expected.areSetToDefaults, pinned.areSetToDefaults, "areSetToDefaults match")
})
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,17 @@ var supportedLanguages = []language{
php, // PHP only works with injection v2, no environment variables are set in any case
}

func defaultSupportedLanguagesMap() map[language]bool {
m := map[language]bool{}
for _, l := range supportedLanguages {
if l.isEnabledByDefault() {
m[l] = true
}
}

return m
}

func (l language) isSupported() bool {
return slices.Contains(supportedLanguages, l)
}
Expand Down
Loading