diff --git a/processor/k8sattributesprocessor/go.mod b/processor/k8sattributesprocessor/go.mod index 7b0d485c1072..cbf81dff640f 100644 --- a/processor/k8sattributesprocessor/go.mod +++ b/processor/k8sattributesprocessor/go.mod @@ -3,6 +3,7 @@ module github.com/open-telemetry/opentelemetry-collector-contrib/processor/k8sat go 1.19 require ( + github.com/google/go-cmp v0.5.9 github.com/google/uuid v1.3.0 github.com/open-telemetry/opentelemetry-collector-contrib/internal/k8sconfig v0.79.0 github.com/open-telemetry/opentelemetry-collector-contrib/internal/k8stest v0.0.0-unpublished @@ -46,7 +47,6 @@ require ( github.com/golang/protobuf v1.5.3 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/gnostic v0.5.7-v3refs // indirect - github.com/google/go-cmp v0.5.9 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/imdario/mergo v0.3.13 // indirect github.com/josharian/intern v1.0.0 // indirect diff --git a/processor/k8sattributesprocessor/internal/metadata/generated_config.go b/processor/k8sattributesprocessor/internal/metadata/generated_config.go new file mode 100644 index 000000000000..a34a0aa675c4 --- /dev/null +++ b/processor/k8sattributesprocessor/internal/metadata/generated_config.go @@ -0,0 +1,101 @@ +// Code generated by mdatagen. DO NOT EDIT. + +package metadata + +// ResourceAttributeConfig provides common config for a particular resource attribute. +type ResourceAttributeConfig struct { + Enabled bool `mapstructure:"enabled"` +} + +// ResourceAttributesConfig provides config for k8sattributes resource attributes. +type ResourceAttributesConfig struct { + ContainerID ResourceAttributeConfig `mapstructure:"container.id"` + ContainerImageName ResourceAttributeConfig `mapstructure:"container.image.name"` + ContainerImageTag ResourceAttributeConfig `mapstructure:"container.image.tag"` + K8sContainerName ResourceAttributeConfig `mapstructure:"k8s.container.name"` + K8sCronjobName ResourceAttributeConfig `mapstructure:"k8s.cronjob.name"` + K8sDaemonsetName ResourceAttributeConfig `mapstructure:"k8s.daemonset.name"` + K8sDaemonsetUID ResourceAttributeConfig `mapstructure:"k8s.daemonset.uid"` + K8sDeploymentName ResourceAttributeConfig `mapstructure:"k8s.deployment.name"` + K8sDeploymentUID ResourceAttributeConfig `mapstructure:"k8s.deployment.uid"` + K8sJobName ResourceAttributeConfig `mapstructure:"k8s.job.name"` + K8sJobUID ResourceAttributeConfig `mapstructure:"k8s.job.uid"` + K8sNamespaceName ResourceAttributeConfig `mapstructure:"k8s.namespace.name"` + K8sNodeName ResourceAttributeConfig `mapstructure:"k8s.node.name"` + K8sPodHostname ResourceAttributeConfig `mapstructure:"k8s.pod.hostname"` + K8sPodName ResourceAttributeConfig `mapstructure:"k8s.pod.name"` + K8sPodStartTime ResourceAttributeConfig `mapstructure:"k8s.pod.start_time"` + K8sPodUID ResourceAttributeConfig `mapstructure:"k8s.pod.uid"` + K8sReplicasetName ResourceAttributeConfig `mapstructure:"k8s.replicaset.name"` + K8sReplicasetUID ResourceAttributeConfig `mapstructure:"k8s.replicaset.uid"` + K8sStatefulsetName ResourceAttributeConfig `mapstructure:"k8s.statefulset.name"` + K8sStatefulsetUID ResourceAttributeConfig `mapstructure:"k8s.statefulset.uid"` +} + +func DefaultResourceAttributesConfig() ResourceAttributesConfig { + return ResourceAttributesConfig{ + ContainerID: ResourceAttributeConfig{ + Enabled: false, + }, + ContainerImageName: ResourceAttributeConfig{ + Enabled: true, + }, + ContainerImageTag: ResourceAttributeConfig{ + Enabled: true, + }, + K8sContainerName: ResourceAttributeConfig{ + Enabled: false, + }, + K8sCronjobName: ResourceAttributeConfig{ + Enabled: false, + }, + K8sDaemonsetName: ResourceAttributeConfig{ + Enabled: false, + }, + K8sDaemonsetUID: ResourceAttributeConfig{ + Enabled: false, + }, + K8sDeploymentName: ResourceAttributeConfig{ + Enabled: true, + }, + K8sDeploymentUID: ResourceAttributeConfig{ + Enabled: false, + }, + K8sJobName: ResourceAttributeConfig{ + Enabled: false, + }, + K8sJobUID: ResourceAttributeConfig{ + Enabled: false, + }, + K8sNamespaceName: ResourceAttributeConfig{ + Enabled: true, + }, + K8sNodeName: ResourceAttributeConfig{ + Enabled: true, + }, + K8sPodHostname: ResourceAttributeConfig{ + Enabled: false, + }, + K8sPodName: ResourceAttributeConfig{ + Enabled: true, + }, + K8sPodStartTime: ResourceAttributeConfig{ + Enabled: true, + }, + K8sPodUID: ResourceAttributeConfig{ + Enabled: true, + }, + K8sReplicasetName: ResourceAttributeConfig{ + Enabled: false, + }, + K8sReplicasetUID: ResourceAttributeConfig{ + Enabled: false, + }, + K8sStatefulsetName: ResourceAttributeConfig{ + Enabled: false, + }, + K8sStatefulsetUID: ResourceAttributeConfig{ + Enabled: false, + }, + } +} diff --git a/processor/k8sattributesprocessor/internal/metadata/generated_config_test.go b/processor/k8sattributesprocessor/internal/metadata/generated_config_test.go new file mode 100644 index 000000000000..b39667725c4a --- /dev/null +++ b/processor/k8sattributesprocessor/internal/metadata/generated_config_test.go @@ -0,0 +1,94 @@ +// Code generated by mdatagen. DO NOT EDIT. + +package metadata + +import ( + "path/filepath" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/confmap/confmaptest" +) + +func TestResourceAttributesConfig(t *testing.T) { + tests := []struct { + name string + want ResourceAttributesConfig + }{ + { + name: "default", + want: DefaultResourceAttributesConfig(), + }, + { + name: "all_set", + want: ResourceAttributesConfig{ + ContainerID: ResourceAttributeConfig{Enabled: true}, + ContainerImageName: ResourceAttributeConfig{Enabled: true}, + ContainerImageTag: ResourceAttributeConfig{Enabled: true}, + K8sContainerName: ResourceAttributeConfig{Enabled: true}, + K8sCronjobName: ResourceAttributeConfig{Enabled: true}, + K8sDaemonsetName: ResourceAttributeConfig{Enabled: true}, + K8sDaemonsetUID: ResourceAttributeConfig{Enabled: true}, + K8sDeploymentName: ResourceAttributeConfig{Enabled: true}, + K8sDeploymentUID: ResourceAttributeConfig{Enabled: true}, + K8sJobName: ResourceAttributeConfig{Enabled: true}, + K8sJobUID: ResourceAttributeConfig{Enabled: true}, + K8sNamespaceName: ResourceAttributeConfig{Enabled: true}, + K8sNodeName: ResourceAttributeConfig{Enabled: true}, + K8sPodHostname: ResourceAttributeConfig{Enabled: true}, + K8sPodName: ResourceAttributeConfig{Enabled: true}, + K8sPodStartTime: ResourceAttributeConfig{Enabled: true}, + K8sPodUID: ResourceAttributeConfig{Enabled: true}, + K8sReplicasetName: ResourceAttributeConfig{Enabled: true}, + K8sReplicasetUID: ResourceAttributeConfig{Enabled: true}, + K8sStatefulsetName: ResourceAttributeConfig{Enabled: true}, + K8sStatefulsetUID: ResourceAttributeConfig{Enabled: true}, + }, + }, + { + name: "none_set", + want: ResourceAttributesConfig{ + ContainerID: ResourceAttributeConfig{Enabled: false}, + ContainerImageName: ResourceAttributeConfig{Enabled: false}, + ContainerImageTag: ResourceAttributeConfig{Enabled: false}, + K8sContainerName: ResourceAttributeConfig{Enabled: false}, + K8sCronjobName: ResourceAttributeConfig{Enabled: false}, + K8sDaemonsetName: ResourceAttributeConfig{Enabled: false}, + K8sDaemonsetUID: ResourceAttributeConfig{Enabled: false}, + K8sDeploymentName: ResourceAttributeConfig{Enabled: false}, + K8sDeploymentUID: ResourceAttributeConfig{Enabled: false}, + K8sJobName: ResourceAttributeConfig{Enabled: false}, + K8sJobUID: ResourceAttributeConfig{Enabled: false}, + K8sNamespaceName: ResourceAttributeConfig{Enabled: false}, + K8sNodeName: ResourceAttributeConfig{Enabled: false}, + K8sPodHostname: ResourceAttributeConfig{Enabled: false}, + K8sPodName: ResourceAttributeConfig{Enabled: false}, + K8sPodStartTime: ResourceAttributeConfig{Enabled: false}, + K8sPodUID: ResourceAttributeConfig{Enabled: false}, + K8sReplicasetName: ResourceAttributeConfig{Enabled: false}, + K8sReplicasetUID: ResourceAttributeConfig{Enabled: false}, + K8sStatefulsetName: ResourceAttributeConfig{Enabled: false}, + K8sStatefulsetUID: ResourceAttributeConfig{Enabled: false}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cm, err := confmaptest.LoadConf(filepath.Join("testdata", "config.yaml")) + require.NoError(t, err) + sub, err := cm.Sub(tt.name) + require.NoError(t, err) + sub, err = sub.Sub("resource_attributes") + require.NoError(t, err) + cfg := DefaultResourceAttributesConfig() + require.NoError(t, component.UnmarshalConfig(sub, &cfg)) + + if diff := cmp.Diff(tt.want, cfg, cmpopts.IgnoreUnexported(ResourceAttributeConfig{})); diff != "" { + t.Errorf("Config mismatch (-expected +actual):\n%s", diff) + } + }) + } +} diff --git a/processor/k8sattributesprocessor/internal/metadata/testdata/config.yaml b/processor/k8sattributesprocessor/internal/metadata/testdata/config.yaml new file mode 100644 index 000000000000..e35bc8699370 --- /dev/null +++ b/processor/k8sattributesprocessor/internal/metadata/testdata/config.yaml @@ -0,0 +1,89 @@ +default: +all_set: + resource_attributes: + container.id: + enabled: true + container.image.name: + enabled: true + container.image.tag: + enabled: true + k8s.container.name: + enabled: true + k8s.cronjob.name: + enabled: true + k8s.daemonset.name: + enabled: true + k8s.daemonset.uid: + enabled: true + k8s.deployment.name: + enabled: true + k8s.deployment.uid: + enabled: true + k8s.job.name: + enabled: true + k8s.job.uid: + enabled: true + k8s.namespace.name: + enabled: true + k8s.node.name: + enabled: true + k8s.pod.hostname: + enabled: true + k8s.pod.name: + enabled: true + k8s.pod.start_time: + enabled: true + k8s.pod.uid: + enabled: true + k8s.replicaset.name: + enabled: true + k8s.replicaset.uid: + enabled: true + k8s.statefulset.name: + enabled: true + k8s.statefulset.uid: + enabled: true +none_set: + resource_attributes: + container.id: + enabled: false + container.image.name: + enabled: false + container.image.tag: + enabled: false + k8s.container.name: + enabled: false + k8s.cronjob.name: + enabled: false + k8s.daemonset.name: + enabled: false + k8s.daemonset.uid: + enabled: false + k8s.deployment.name: + enabled: false + k8s.deployment.uid: + enabled: false + k8s.job.name: + enabled: false + k8s.job.uid: + enabled: false + k8s.namespace.name: + enabled: false + k8s.node.name: + enabled: false + k8s.pod.hostname: + enabled: false + k8s.pod.name: + enabled: false + k8s.pod.start_time: + enabled: false + k8s.pod.uid: + enabled: false + k8s.replicaset.name: + enabled: false + k8s.replicaset.uid: + enabled: false + k8s.statefulset.name: + enabled: false + k8s.statefulset.uid: + enabled: false diff --git a/processor/k8sattributesprocessor/metadata.yaml b/processor/k8sattributesprocessor/metadata.yaml index 79e008734cb9..49ce53adfa54 100644 --- a/processor/k8sattributesprocessor/metadata.yaml +++ b/processor/k8sattributesprocessor/metadata.yaml @@ -5,3 +5,90 @@ status: stability: beta: [logs, metrics, traces] distributions: [contrib, splunk, observiq, sumo] + +# resource attributes are exposed through a different configuration interface (extract::metadata). +resource_attributes: + k8s.namespace.name: + description: The name of the namespace that the pod is running in. + type: string + enabled: true + k8s.pod.name: + description: The name of the Pod. + type: string + enabled: true + k8s.pod.uid: + description: The UID of the Pod. + type: string + enabled: true + k8s.pod.hostname: + description: The hostname of the Pod. + type: string + enabled: false + k8s.pod.start_time: + description: The start time of the Pod. + type: string + enabled: true + k8s.deployment.name: + description: The name of the Deployment. + type: string + enabled: true + k8s.deployment.uid: + description: The UID of the Deployment. + type: string + enabled: false + k8s.replicaset.name: + description: The name of the ReplicaSet. + type: string + enabled: false + k8s.replicaset.uid: + description: The UID of the ReplicaSet. + type: string + enabled: false + k8s.daemonset.name: + description: The name of the DaemonSet. + type: string + enabled: false + k8s.daemonset.uid: + description: The UID of the DaemonSet. + type: string + enabled: false + k8s.statefulset.name: + description: The name of the StatefulSet. + type: string + enabled: false + k8s.statefulset.uid: + description: The UID of the StatefulSet. + type: string + enabled: false + k8s.container.name: + description: The name of the Container in a Pod template. Requires container.id. + type: string + enabled: false + k8s.job.name: + description: The name of the Job. + type: string + enabled: false + k8s.job.uid: + description: The UID of the Job. + type: string + enabled: false + k8s.cronjob.name: + description: The name of the CronJob. + type: string + enabled: false + k8s.node.name: + description: The name of the Node. + type: string + enabled: true + container.id: + description: Container ID. Usually a UUID, as for example used to identify Docker containers. The UUID might be abbreviated. Requires k8s.container.restart_count. + type: string + enabled: false + container.image.name: + description: Name of the image the container was built on. Requires container.id or k8s.container.name. + type: string + enabled: true + container.image.tag: + description: Container image tag. Requires container.id or k8s.container.name. + type: string + enabled: true diff --git a/processor/k8sattributesprocessor/options.go b/processor/k8sattributesprocessor/options.go index fe156ac422e1..75abb9b7d495 100644 --- a/processor/k8sattributesprocessor/options.go +++ b/processor/k8sattributesprocessor/options.go @@ -13,6 +13,7 @@ import ( "github.com/open-telemetry/opentelemetry-collector-contrib/internal/k8sconfig" "github.com/open-telemetry/opentelemetry-collector-contrib/processor/k8sattributesprocessor/internal/kube" + "github.com/open-telemetry/opentelemetry-collector-contrib/processor/k8sattributesprocessor/internal/metadata" ) const ( @@ -46,21 +47,81 @@ func withPassthrough() option { } } +// enabledAttributes returns the list of resource attributes enabled by default. +func enabledAttributes() (attributes []string) { + defaultConfig := metadata.DefaultResourceAttributesConfig() + if defaultConfig.ContainerID.Enabled { + attributes = append(attributes, conventions.AttributeContainerID) + } + if defaultConfig.ContainerImageName.Enabled { + attributes = append(attributes, conventions.AttributeContainerImageName) + } + if defaultConfig.ContainerImageTag.Enabled { + attributes = append(attributes, conventions.AttributeContainerImageTag) + } + if defaultConfig.K8sContainerName.Enabled { + attributes = append(attributes, conventions.AttributeK8SContainerName) + } + if defaultConfig.K8sCronjobName.Enabled { + attributes = append(attributes, conventions.AttributeK8SCronJobName) + } + if defaultConfig.K8sDaemonsetName.Enabled { + attributes = append(attributes, conventions.AttributeK8SDaemonSetName) + } + if defaultConfig.K8sDaemonsetUID.Enabled { + attributes = append(attributes, conventions.AttributeK8SDaemonSetUID) + } + if defaultConfig.K8sDeploymentName.Enabled { + attributes = append(attributes, conventions.AttributeK8SDeploymentName) + } + if defaultConfig.K8sDeploymentUID.Enabled { + attributes = append(attributes, conventions.AttributeK8SDeploymentUID) + } + if defaultConfig.K8sJobName.Enabled { + attributes = append(attributes, conventions.AttributeK8SJobName) + } + if defaultConfig.K8sJobUID.Enabled { + attributes = append(attributes, conventions.AttributeK8SJobUID) + } + if defaultConfig.K8sNamespaceName.Enabled { + attributes = append(attributes, conventions.AttributeK8SNamespaceName) + } + if defaultConfig.K8sNodeName.Enabled { + attributes = append(attributes, conventions.AttributeK8SNodeName) + } + if defaultConfig.K8sPodHostname.Enabled { + attributes = append(attributes, specPodHostName) + } + if defaultConfig.K8sPodName.Enabled { + attributes = append(attributes, conventions.AttributeK8SPodName) + } + if defaultConfig.K8sPodStartTime.Enabled { + attributes = append(attributes, metadataPodStartTime) + } + if defaultConfig.K8sPodUID.Enabled { + attributes = append(attributes, conventions.AttributeK8SPodUID) + } + if defaultConfig.K8sReplicasetName.Enabled { + attributes = append(attributes, conventions.AttributeK8SReplicaSetName) + } + if defaultConfig.K8sReplicasetUID.Enabled { + attributes = append(attributes, conventions.AttributeK8SReplicaSetUID) + } + if defaultConfig.K8sStatefulsetName.Enabled { + attributes = append(attributes, conventions.AttributeK8SStatefulSetName) + } + if defaultConfig.K8sStatefulsetUID.Enabled { + attributes = append(attributes, conventions.AttributeK8SStatefulSetUID) + } + return +} + // withExtractMetadata allows specifying options to control extraction of pod metadata. -// If no fields explicitly provided, all metadata extracted by default. +// If no fields explicitly provided, the defaults are pulled from metadata.yaml. func withExtractMetadata(fields ...string) option { return func(p *kubernetesprocessor) error { if len(fields) == 0 { - fields = []string{ - conventions.AttributeK8SNamespaceName, - conventions.AttributeK8SPodName, - conventions.AttributeK8SPodUID, - metadataPodStartTime, - conventions.AttributeK8SDeploymentName, - conventions.AttributeK8SNodeName, - conventions.AttributeContainerImageName, - conventions.AttributeContainerImageTag, - } + fields = enabledAttributes() } for _, field := range fields { switch field { diff --git a/processor/k8sattributesprocessor/options_test.go b/processor/k8sattributesprocessor/options_test.go index 15970365d533..714cfbe43302 100644 --- a/processor/k8sattributesprocessor/options_test.go +++ b/processor/k8sattributesprocessor/options_test.go @@ -56,6 +56,21 @@ func TestWithPassthrough(t *testing.T) { assert.True(t, p.passthroughMode) } +func TestEnabledAttributes(t *testing.T) { + // This list needs to be updated when the defaults in metadata.yaml are updated. + expected := []string{ + conventions.AttributeK8SNamespaceName, + conventions.AttributeK8SPodName, + conventions.AttributeK8SPodUID, + metadataPodStartTime, + conventions.AttributeK8SDeploymentName, + conventions.AttributeK8SNodeName, + conventions.AttributeContainerImageName, + conventions.AttributeContainerImageTag, + } + assert.ElementsMatch(t, expected, enabledAttributes()) +} + func TestWithExtractAnnotations(t *testing.T) { tests := []struct { name string