Skip to content

Commit

Permalink
expose kubernetes_resources_labels/annotations_as_tags (#1379)
Browse files Browse the repository at this point in the history
* expose kubernetes_resources_labels/annotations_as_tags

* fix lint

* rename clusterRole

---------

Co-authored-by: Fanny Jiang <fanny.jiang@datadoghq.com>
  • Loading branch information
adel121 and fanny-jiang authored Oct 10, 2024
1 parent fe0188f commit 26068ba
Show file tree
Hide file tree
Showing 10 changed files with 267 additions and 0 deletions.
2 changes: 2 additions & 0 deletions api/datadoghq/common/envvar.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ const (
DDNamespaceLabelsAsTags = "DD_KUBERNETES_NAMESPACE_LABELS_AS_TAGS"
DDNamespaceAnnotationsAsTags = "DD_KUBERNETES_NAMESPACE_ANNOTATIONS_AS_TAGS"
DDNodeLabelsAsTags = "DD_KUBERNETES_NODE_LABELS_AS_TAGS"
DDKubernetesResourcesLabelsAsTags = "DD_KUBERNETES_RESOURCES_LABELS_AS_TAGS"
DDKubernetesResourcesAnnotationsAsTags = "DD_KUBERNETES_RESOURCES_ANNOTATIONS_AS_TAGS"
DDOrchestratorExplorerEnabled = "DD_ORCHESTRATOR_EXPLORER_ENABLED"
DDOrchestratorExplorerExtraTags = "DD_ORCHESTRATOR_EXPLORER_EXTRA_TAGS"
DDOrchestratorExplorerDDUrl = "DD_ORCHESTRATOR_EXPLORER_ORCHESTRATOR_DD_URL"
Expand Down
14 changes: 14 additions & 0 deletions api/datadoghq/v2alpha1/datadogagent_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -1222,6 +1222,20 @@ type GlobalConfig struct {
// +optional
NamespaceAnnotationsAsTags map[string]string `json:"namespaceAnnotationsAsTags,omitempty"`

// Provide a mapping of Kubernetes Resource Groups to labels mapping to Datadog Tags.
// <KUBERNETES_RESOURCE_GROUP>:
// <KUBERNETES_LABEL>: <DATADOG_TAG_KEY>
// KUBERNETES_RESOURCE_GROUP should be in the form `{resource}.{group}` or `{resource}` (example: deployments.apps, pods)
// +optional
KubernetesResourcesLabelsAsTags map[string]map[string]string `json:"kubernetesResourcesLabelsAsTags,omitempty"`

// Provide a mapping of Kubernetes Resource Groups to annotations mapping to Datadog Tags.
// <KUBERNETES_RESOURCE_GROUP>:
// <KUBERNETES_ANNOTATION>: <DATADOG_TAG_KEY>
// KUBERNETES_RESOURCE_GROUP should be in the form `{resource}.{group}` or `{resource}` (example: deployments.apps, pods)
// +optional
KubernetesResourcesAnnotationsAsTags map[string]map[string]string `json:"kubernetesResourcesAnnotationsAsTags,omitempty"`

// NetworkPolicy contains the network configuration.
// +optional
NetworkPolicy *NetworkPolicyConfig `json:"networkPolicy,omitempty"`
Expand Down
36 changes: 36 additions & 0 deletions api/datadoghq/v2alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 22 additions & 0 deletions config/crd/bases/v1/datadoghq.com_datadogagents.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1949,6 +1949,28 @@ spec:
Default: true
type: boolean
type: object
kubernetesResourcesAnnotationsAsTags:
additionalProperties:
additionalProperties:
type: string
type: object
description: |-
Provide a mapping of Kubernetes Resource Groups to annotations mapping to Datadog Tags.
<KUBERNETES_RESOURCE_GROUP>:
<KUBERNETES_ANNOTATION>: <DATADOG_TAG_KEY>
KUBERNETES_RESOURCE_GROUP should be in the form `{resource}.{group}` or `{resource}` (example: deployments.apps, pods)
type: object
kubernetesResourcesLabelsAsTags:
additionalProperties:
additionalProperties:
type: string
type: object
description: |-
Provide a mapping of Kubernetes Resource Groups to labels mapping to Datadog Tags.
<KUBERNETES_RESOURCE_GROUP>:
<KUBERNETES_LABEL>: <DATADOG_TAG_KEY>
KUBERNETES_RESOURCE_GROUP should be in the form `{resource}.{group}` or `{resource}` (example: deployments.apps, pods)
type: object
localService:
description: LocalService contains configuration to customize the internal traffic policy service.
properties:
Expand Down
2 changes: 2 additions & 0 deletions docs/configuration.v2alpha1.md
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,8 @@ spec:
| global.kubelet.host.secretKeyRef.optional | Specify whether the Secret or its key must be defined |
| global.kubelet.hostCAPath | HostCAPath is the host path where the kubelet CA certificate is stored. |
| global.kubelet.tlsVerify | TLSVerify toggles kubelet TLS verification. Default: true |
| global.kubernetesResourcesAnnotationsAsTags | Provide a mapping of Kubernetes Resource Groups to annotations mapping to Datadog Tags. <KUBERNETES_RESOURCE_GROUP>: <KUBERNETES_ANNOTATION>: <DATADOG_TAG_KEY> KUBERNETES_RESOURCE_GROUP should be in the form `{resource}.{group}` or `{resource}` (example: deployments.apps, pods) |
| global.kubernetesResourcesLabelsAsTags | Provide a mapping of Kubernetes Resource Groups to labels mapping to Datadog Tags. <KUBERNETES_RESOURCE_GROUP>: <KUBERNETES_LABEL>: <DATADOG_TAG_KEY> KUBERNETES_RESOURCE_GROUP should be in the form `{resource}.{group}` or `{resource}` (example: deployments.apps, pods) |
| global.localService.forceEnableLocalService | ForceEnableLocalService forces the creation of the internal traffic policy service to target the agent running on the local node. This parameter only applies to Kubernetes 1.21, where the feature is in alpha and is disabled by default. (On Kubernetes 1.22+, the feature entered beta and the internal traffic service is created by default, so this parameter is ignored.) Default: false |
| global.localService.nameOverride | NameOverride defines the name of the internal traffic service to target the agent running on the local node. |
| global.logLevel | LogLevel sets logging verbosity. This can be overridden by container. Valid log levels are: trace, debug, info, warn, error, critical, and off. Default: 'info' |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@ func GetExternalMetricsReaderClusterRoleName(dda metav1.Object, versionInfo *ver
return fmt.Sprintf("%s-metrics-reader", GetClusterAgentRbacResourcesName(dda))
}

// GetResourceMetadataAsTagsClusterRoleName returns the name for the cluster role name used for kubernetes resource labels and annotations as tags
func GetResourceMetadataAsTagsClusterRoleName(dda metav1.Object) string {
return fmt.Sprintf("%s-annotations-and-labels-as-tags", GetClusterAgentRbacResourcesName(dda))
}

// GetApiserverAuthReaderRoleBindingName returns the name for the role binding to access the extension-apiserver-authentication cm
func GetApiserverAuthReaderRoleBindingName(dda metav1.Object) string {
return fmt.Sprintf("%s-apiserver", GetClusterAgentRbacResourcesName(dda))
Expand Down
38 changes: 38 additions & 0 deletions internal/controller/datadogagent/override/global.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
apicommon "github.com/DataDog/datadog-operator/api/datadoghq/common"
"github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1"
apiutils "github.com/DataDog/datadog-operator/api/utils"
componentdca "github.com/DataDog/datadog-operator/internal/controller/datadogagent/component/clusteragent"
"github.com/DataDog/datadog-operator/internal/controller/datadogagent/component/objects"
"github.com/DataDog/datadog-operator/internal/controller/datadogagent/feature"
"github.com/DataDog/datadog-operator/internal/controller/datadogagent/object/volume"
Expand Down Expand Up @@ -219,6 +220,43 @@ func applyGlobalSettings(logger logr.Logger, manager feature.PodTemplateManagers
}
}

// Provide a mapping of Kubernetes Resource Labels to Datadog Tags.
if config.KubernetesResourcesLabelsAsTags != nil {
kubernetesResourceLabelsAsTags, err := json.Marshal(config.KubernetesResourcesLabelsAsTags)
if err != nil {
logger.Error(err, "Failed to unmarshal json input")
} else {
manager.EnvVar().AddEnvVar(&corev1.EnvVar{
Name: apicommon.DDKubernetesResourcesLabelsAsTags,
Value: string(kubernetesResourceLabelsAsTags),
})
}
}

// Provide a mapping of Kubernetes Resource Annotations to Datadog Tags.
if config.KubernetesResourcesLabelsAsTags != nil {
kubernetesResourceAnnotationsAsTags, err := json.Marshal(config.KubernetesResourcesAnnotationsAsTags)
if err != nil {
logger.Error(err, "Failed to unmarshal json input")
} else {
manager.EnvVar().AddEnvVar(&corev1.EnvVar{
Name: apicommon.DDKubernetesResourcesAnnotationsAsTags,
Value: string(kubernetesResourceAnnotationsAsTags),
})
}
}

if componentName == v2alpha1.ClusterAgentComponentName {
if err := resourcesManager.RBACManager().AddClusterPolicyRules(
dda.Namespace,
componentdca.GetResourceMetadataAsTagsClusterRoleName(dda),
v2alpha1.GetClusterAgentServiceAccount(dda),
getKubernetesResourceMetadataAsTagsPolicyRules(config.KubernetesResourcesLabelsAsTags, config.KubernetesResourcesAnnotationsAsTags),
); err != nil {
logger.Error(err, "error adding kubernetes resource metadata as tags clusterrole and clusterrolebinding to store")
}
}

if componentName == v2alpha1.NodeAgentComponentName {
// Kubelet contains the kubelet configuration parameters.
// The environment variable `DD_KUBERNETES_KUBELET_HOST` defaults to `status.hostIP` if not overriden.
Expand Down
79 changes: 79 additions & 0 deletions internal/controller/datadogagent/override/rbac.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2016-present Datadog, Inc.

package override

import (
"strings"

"github.com/DataDog/datadog-operator/pkg/kubernetes/rbac"
rbacv1 "k8s.io/api/rbac/v1"
)

func extractGroupAndResource(groupResource string) (group string, resource string, ok bool) {
parts := strings.Split(groupResource, ".")

switch len(parts) {
case 1:
group = ""
resource = parts[0]
ok = true
case 2:
group = parts[1]
resource = parts[0]
ok = true
default:
ok = false
}

return group, resource, ok
}

func appendGroupResource(groupResourceAccumulator map[string]map[string]struct{}, group string, resource string) map[string]map[string]struct{} {
if _, exists := groupResourceAccumulator[group]; !exists {
groupResourceAccumulator[group] = map[string]struct{}{resource: {}}
} else {
groupResourceAccumulator[group][resource] = struct{}{}
}

return groupResourceAccumulator
}

func getKubernetesResourceMetadataAsTagsPolicyRules(resourcesLabelsAsTags, resourcesAnnotationsAsTags map[string]map[string]string) []rbacv1.PolicyRule {
// maps group to resource set
// using map to avoid duplicates
groupResourceAccumulator := map[string]map[string]struct{}{}

for groupResource := range resourcesLabelsAsTags {
if group, resource, ok := extractGroupAndResource(groupResource); ok {
groupResourceAccumulator = appendGroupResource(groupResourceAccumulator, group, resource)
}
}

for groupResource := range resourcesAnnotationsAsTags {
if group, resource, ok := extractGroupAndResource(groupResource); ok {
groupResourceAccumulator = appendGroupResource(groupResourceAccumulator, group, resource)
}
}

policyRules := make([]rbacv1.PolicyRule, 0)

for group, resources := range groupResourceAccumulator {
for resource := range resources {
policyRules = append(policyRules, rbacv1.PolicyRule{
APIGroups: []string{group},
Resources: []string{resource},
Verbs: []string{
rbac.GetVerb,
rbac.ListVerb,
rbac.WatchVerb,
},
},
)
}
}

return policyRules
}
63 changes: 63 additions & 0 deletions internal/controller/datadogagent/override/rbac_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2016-present Datadog, Inc.

package override

import (
"testing"

"github.com/DataDog/datadog-operator/pkg/kubernetes/rbac"
assert "github.com/stretchr/testify/require"
rbacv1 "k8s.io/api/rbac/v1"
)

func TestGetKubernetesResourceMetadataAsTagsPolicyRules(t *testing.T) {
labelsAsTags := map[string]map[string]string{
"pods": {
"foo": "bar",
"bar": "bar",
},
"deployments.apps": {
"foo": "baz",
"bar": "bar",
},
}

annotationsAsTags := map[string]map[string]string{
"pods": {
"foo": "bar",
"bar": "bar",
},
"deployments.apps": {
"foo": "baz",
"bar": "bar",
},
}

expectedRules := []rbacv1.PolicyRule{
{
APIGroups: []string{""},
Resources: []string{"pods"},
Verbs: []string{
rbac.GetVerb,
rbac.ListVerb,
rbac.WatchVerb,
},
},
{
APIGroups: []string{"apps"},
Resources: []string{"deployments"},
Verbs: []string{
rbac.GetVerb,
rbac.ListVerb,
rbac.WatchVerb,
},
},
}

rules := getKubernetesResourceMetadataAsTagsPolicyRules(labelsAsTags, annotationsAsTags)

assert.ElementsMatch(t, expectedRules, rules)
}
6 changes: 6 additions & 0 deletions internal/controller/testutils/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,12 @@ func NewDatadogAgentWithGlobalConfigSettings(namespace string, name string) v2al
NodeLabelsAsTags: map[string]string{"some-label": "some-tag"},
NamespaceLabelsAsTags: map[string]string{"some-label": "some-tag"},
NamespaceAnnotationsAsTags: map[string]string{"some-annotation": "some-tag"},
KubernetesResourcesLabelsAsTags: map[string]map[string]string{
"some-group.some-resource": {"some-label": "some-tag"},
},
KubernetesResourcesAnnotationsAsTags: map[string]map[string]string{
"some-group.some-resource": {"some-annotation": "some-tag"},
},
NetworkPolicy: &v2alpha1.NetworkPolicyConfig{
Create: apiutils.NewBoolPointer(true),
Flavor: v2alpha1.NetworkPolicyFlavorKubernetes,
Expand Down

0 comments on commit 26068ba

Please sign in to comment.