Skip to content

Commit

Permalink
feat(ssi): enable namespace collection if ssi is enabled
Browse files Browse the repository at this point in the history
This commit adds namespace metadata collection if auto instrumentation
is enabled with a target list defined.
  • Loading branch information
betterengineering committed Feb 9, 2025
1 parent bd084ae commit bec81f8
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,13 @@ func resourcesWithRequiredMetadataCollection(cfg config.Reader) []string {
}
}

for _, groupResource := range resourcesForAPMConfig(cfg) {
requestedResource := groupResourceToGVRString(groupResource)
if requestedResource != "" {
res = append(res, requestedResource)
}
}

return res
}

Expand Down Expand Up @@ -146,6 +153,34 @@ func resourcesWithExplicitMetadataCollectionEnabled(cfg config.Reader) []string
return resources
}

// resourcesForAPMConfig returns the list of resources to collect metadata from
// for the auto instrumentation configuration. Namespaces are collected in order
// to utilize namespace labels for target based configuration.
func resourcesForAPMConfig(cfg config.Reader) []string {
// If APM is not enabled, we don't need to collect any resources for the
// auto instrumentation configuration.
apmEnabled := cfg.GetBool("apm_config.instrumentation.enabled")
if !apmEnabled {
return nil
}

// Targets is a custom struct type, so we unmarshal it into an interface
// slice to avoid the import while still being able to check if it's empty.
targets := []interface{}{}
err := cfg.UnmarshalKey("apm_config.instrumentation.targets", &targets)
if err != nil {
log.Errorf("failed to unmarshal apm_config.instrumentation.targets: %v", err)
return nil
}

// If there are no targets, we don't need to collect any resources.
if len(targets) == 0 {
return nil
}

return []string{"namespaces"}
}

type collector struct {
id string
catalog workloadmeta.AgentType
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,25 @@ func TestResourcesWithMetadataCollectionEnabled(t *testing.T) {
},
expectedResources: []string{"//nodes", "//namespaces"}, // namespaces are not duplicated
},
{
name: "resources explicitly requested with apm enabled and also needed for namespace labels as tags",
cfg: map[string]interface{}{
"apm_config.instrumentation.enabled": true,
"apm_config.instrumentation.targets": []interface{}{"target-1"},
"cluster_agent.kube_metadata_collection.enabled": true,
"cluster_agent.kube_metadata_collection.resources": "namespaces apps/deployments",
"kubernetes_namespace_labels_as_tagkubernetes_namespace_labels_as_tagss": `{"label1": "tag1"}`,
},
expectedResources: []string{"//nodes", "//namespaces"}, // namespaces are not duplicated
},
{
name: "apm enabled enables namespace collection",
cfg: map[string]interface{}{
"apm_config.instrumentation.enabled": true,
"apm_config.instrumentation.targets": []interface{}{"target-1"},
},
expectedResources: []string{"//nodes", "//namespaces"},
},
}

for _, test := range tests {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (

"github.com/DataDog/datadog-agent/comp/core/workloadmeta/collectors/util"
workloadmeta "github.com/DataDog/datadog-agent/comp/core/workloadmeta/def"
"github.com/DataDog/datadog-agent/pkg/trace/log"
)

// TargetFilter filters pods based on a set of targeting rules.
Expand Down Expand Up @@ -110,7 +111,13 @@ func (f *TargetFilter) filter(pod *corev1.Pod) []libInfo {
// Check if the pod matches any of the targets. The first match wins.
for _, target := range f.targets {
// Check the pod namespace against the namespace selector.
if !target.matchesNamespaceSelector(pod.Namespace) {
matches, err := target.matchesNamespaceSelector(pod.Namespace)
if err != nil {
log.Errorf("error encountered matching targets, aborting all together to avoid inaccurate match: %w", err)
return nil

}
if !matches {
continue
}

Expand All @@ -127,29 +134,28 @@ func (f *TargetFilter) filter(pod *corev1.Pod) []libInfo {
return nil
}

func (t targetInternal) matchesNamespaceSelector(namespace string) bool {
func (t targetInternal) matchesNamespaceSelector(namespace string) (bool, error) {
// If we are using the namespace selector, check if the namespace matches the selector.
if t.useNamespaceSelector {
// Get the namespace metadata. At the time of writing, this method will only return an error if the namespace
// does not exist, in which case we return false for this selector.
// Get the namespace metadata.
id := util.GenerateKubeMetadataEntityID("", "namespaces", "", namespace)
meta, _ := t.wmeta.GetKubernetesMetadata(id)
if meta == nil {
return false
ns, err := t.wmeta.GetKubernetesMetadata(id)
if err != nil {
return false, fmt.Errorf("could not get kubernetes namespace to match against for %s: %w", namespace, err)
}

// Check if the namespace labels match the selector.
return t.nameSpaceSelector.Matches(labels.Set(meta.EntityMeta.Labels))
return t.nameSpaceSelector.Matches(labels.Set(ns.EntityMeta.Labels)), nil
}

// If there are no match names, we match all namespaces.
if len(t.enabledNamespaces) == 0 {
return true
return true, nil
}

// Check if the pod namespace is in the match names.
_, ok := t.enabledNamespaces[namespace]
return ok
return ok, nil
}

func (t targetInternal) matchesPodSelector(podLabels map[string]string) bool {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ func TestTargetFilter(t *testing.T) {
},
},
},
namespaces: []workloadmeta.KubernetesMetadata{
newTestNamespace("default", nil),
},
expected: []libInfo{
{
ctrName: "",
Expand All @@ -61,6 +64,9 @@ func TestTargetFilter(t *testing.T) {
},
},
},
namespaces: []workloadmeta.KubernetesMetadata{
newTestNamespace("default", nil),
},
expected: nil,
},
"a single service example matches rule": {
Expand All @@ -73,6 +79,9 @@ func TestTargetFilter(t *testing.T) {
},
},
},
namespaces: []workloadmeta.KubernetesMetadata{
newTestNamespace("billing-service", nil),
},
expected: []libInfo{
{
ctrName: "",
Expand All @@ -91,6 +100,9 @@ func TestTargetFilter(t *testing.T) {
},
},
},
namespaces: []workloadmeta.KubernetesMetadata{
newTestNamespace("application", nil),
},
expected: []libInfo{
{
ctrName: "",
Expand All @@ -109,6 +121,9 @@ func TestTargetFilter(t *testing.T) {
},
},
},
namespaces: []workloadmeta.KubernetesMetadata{
newTestNamespace("infra", nil),
},
expected: nil,
},
"namespace labels are used to match namespaces": {
Expand All @@ -120,19 +135,10 @@ func TestTargetFilter(t *testing.T) {
},
},
namespaces: []workloadmeta.KubernetesMetadata{
{
EntityID: workloadmeta.EntityID{
Kind: workloadmeta.KindKubernetesMetadata,
ID: string(util.GenerateKubeMetadataEntityID("", "namespaces", "", "foo")),
},
EntityMeta: workloadmeta.EntityMeta{
Name: "foo",
Labels: map[string]string{
"tracing": "yes",
"env": "prod",
},
},
},
newTestNamespace("foo", map[string]string{
"tracing": "yes",
"env": "prod",
}),
},
expected: []libInfo{
{
Expand All @@ -151,18 +157,19 @@ func TestTargetFilter(t *testing.T) {
},
},
namespaces: []workloadmeta.KubernetesMetadata{
{
EntityID: workloadmeta.EntityID{
Kind: workloadmeta.KindKubernetesMetadata,
ID: string(util.GenerateKubeMetadataEntityID("", "namespaces", "", "foo")),
},
EntityMeta: workloadmeta.EntityMeta{
Name: "foo",
Labels: map[string]string{
"tracing": "yes",
"env": "prod",
},
},
newTestNamespace("foo", map[string]string{
"tracing": "yes",
"env": "prod",
}),
},
expected: nil,
},
"missing namespace in store gets no tracers": {
configPath: "testdata/filter.yaml",
in: &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Namespace: "foo",
Labels: map[string]string{},
},
},
expected: nil,
Expand All @@ -177,6 +184,9 @@ func TestTargetFilter(t *testing.T) {
},
},
},
namespaces: []workloadmeta.KubernetesMetadata{
newTestNamespace("application", nil),
},
expected: []libInfo{
{
ctrName: "",
Expand Down Expand Up @@ -239,3 +249,16 @@ func TestTargetFilter(t *testing.T) {
})
}
}

func newTestNamespace(name string, labels map[string]string) workloadmeta.KubernetesMetadata {
return workloadmeta.KubernetesMetadata{
EntityID: workloadmeta.EntityID{
Kind: workloadmeta.KindKubernetesMetadata,
ID: string(util.GenerateKubeMetadataEntityID("", "namespaces", "", name)),
},
EntityMeta: workloadmeta.EntityMeta{
Name: name,
Labels: labels,
},
}
}

0 comments on commit bec81f8

Please sign in to comment.