Skip to content
Draft
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
14 changes: 14 additions & 0 deletions processor/k8sattributesprocessor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -539,8 +539,22 @@ timestamp value as an RFC3339 compliant timestamp.

## Feature Gates

### `semconv.k8s.k8sattributes.enableStable`

The `semconv.k8s.k8sattributes.enableStable` [feature gate](https://github.com/open-telemetry/opentelemetry-collector/blob/main/featuregate/README.md#collector-feature-gates) enables the SemConv valid format of the k8s attributes (`k8s.<workload>.label.<label-key>` and `k8s.<workload>.annotation.<annotation-key>`).

The feature gate is in `alpha` stage, which means it is disabled by default.

### `semconv.k8s.k8sattributes.disableLegacy`

The `semconv.k8s.k8sattributes.disableLegacy` [feature gate](https://github.com/open-telemetry/opentelemetry-collector/blob/main/featuregate/README.md#collector-feature-gates) disables the old, non-SemConv valid format of the k8s attributes (`k8s.<workload>.labels.<label-key>` and `k8s.<workload>.annotations.<annotation-key>`).

The feature gate is in `alpha` stage, which means it is disabled by default.

### `k8sattr.labelsAnnotationsSingular.allow`

**Deprecated:** Use `semconv.k8s.k8sattributes.enableStable` instead.

The `k8sattr.labelsAnnotationsSingular.allow` feature gate, when enabled, changes the default resource attribute key format from `k8s.<workload>.labels.<label-key>` to `k8s.<workload>.label.<label-key>` and `k8s.<workload>.annotations.<annotation-key>` to `k8s.<workload>.annotation.<annotation-key>`.

This affects both:
Expand Down
1 change: 1 addition & 0 deletions processor/k8sattributesprocessor/documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
| container.image.name | Name of the image the container was built on. Requires container.id or k8s.container.name. | Any Str | true |
| container.image.repo_digests | Repo digests of the container image as provided by the container runtime. | Any Slice | false |
| container.image.tag | Container image tag. Defaults to "latest" if not provided (unless digest also in image path) Requires container.id or k8s.container.name. | Any Str | true |
| container.image.tags | Container image tags. | Any Slice | true |
| k8s.cluster.uid | Gives cluster uid identified with kube-system namespace | Any Str | false |
| k8s.container.name | The name of the Container in a Pod template. Requires container.id. | Any Str | false |
| k8s.cronjob.name | The name of the CronJob. | Any Str | false |
Expand Down
120 changes: 79 additions & 41 deletions processor/k8sattributesprocessor/internal/kube/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,26 @@ const (
K8sJobAnnotation = "k8s.job.annotation.%s"
)

var AllowLabelsAnnotationsSingular = featuregate.GlobalRegistry().MustRegister(
"k8sattr.labelsAnnotationsSingular.allow",
featuregate.StageAlpha,
featuregate.WithRegisterDescription("When enabled, default k8s label and annotation resource attribute keys will be singular, instead of plural"),
featuregate.WithRegisterFromVersion("v0.125.0"),
var (
EnableStableAttributes = featuregate.GlobalRegistry().MustRegister(
"semconv.k8s.k8sattributes.enableStable",
featuregate.StageAlpha,
featuregate.WithRegisterDescription("When enabled, semconv stable attributes are enabled."),
featuregate.WithRegisterFromVersion("v0.144.0"),
)
DisableLegacyAttributes = featuregate.GlobalRegistry().MustRegister(
"semconv.k8s.k8sattributes.disableLegacy",
featuregate.StageAlpha,
featuregate.WithRegisterDescription("When enabled, semconv legacy attributes are disabled."),
featuregate.WithRegisterFromVersion("v0.144.0"),
)
AllowLabelsAnnotationsSingular = featuregate.GlobalRegistry().MustRegister(
"k8sattr.labelsAnnotationsSingular.allow",
featuregate.StageDeprecated,
featuregate.WithRegisterDescription("When enabled, default k8s label and annotation resource attribute keys will be singular, instead of plural"),
featuregate.WithRegisterFromVersion("v0.125.0"),
featuregate.WithRegisterToVersion("v0.150.0"),
)
)

// WatchClient is the main interface provided by this package to a kubernetes cluster.
Expand Down Expand Up @@ -926,18 +941,25 @@ func (c *WatchClient) extractPodAttributes(pod *api_v1.Pod) map[string]string {
}
}

formatterLabel := K8sPodLabelsKey
if AllowLabelsAnnotationsSingular.IsEnabled() {
formatterLabel = K8sPodLabelKey
}
enableStable := EnableStableAttributes.IsEnabled()
disableLegacy := DisableLegacyAttributes.IsEnabled()

for _, r := range c.Rules.Labels {
r.extractFromPodMetadata(pod.Labels, tags, formatterLabel)
if !disableLegacy {
r.extractFromPodMetadata(pod.Labels, tags, K8sPodLabelsKey)
}
if enableStable {
r.extractFromPodMetadata(pod.Labels, tags, K8sPodLabelKey)
}
}

formatterAnnotation := K8sPodAnnotationsKey
if AllowLabelsAnnotationsSingular.IsEnabled() {
formatterAnnotation = K8sPodAnnotationKey
for _, r := range c.Rules.Annotations {
if !disableLegacy {
r.extractFromPodMetadata(pod.Annotations, tags, K8sPodAnnotationsKey)
}
if enableStable {
r.extractFromPodMetadata(pod.Annotations, tags, K8sPodAnnotationKey)
}
}

if c.Rules.ServiceName {
Expand All @@ -950,9 +972,6 @@ func (c *WatchClient) extractPodAttributes(pod *api_v1.Pod) map[string]string {
copyLabel(pod, tags, "app.kubernetes.io/version", conventions.ServiceVersionKey)
}

for _, r := range c.Rules.Annotations {
r.extractFromPodMetadata(pod.Annotations, tags, formatterAnnotation)
}
return tags
}

Expand Down Expand Up @@ -1026,7 +1045,8 @@ func removeUnnecessaryPodData(pod *api_v1.Pod, rules ExtractionRules) *api_v1.Po
removeUnnecessaryContainerData := func(c api_v1.Container) api_v1.Container {
transformedContainer := api_v1.Container{}
transformedContainer.Name = c.Name // we always need the name, it's used for identification
if rules.ContainerImageName || rules.ContainerImageTag || rules.ServiceVersion {
// Need image if extracting name, tag (legacy or stable), or service version
if rules.ContainerImageName || rules.ContainerImageTag || rules.ContainerImageTags || rules.ServiceVersion {
transformedContainer.Image = c.Image
}
return transformedContainer
Expand Down Expand Up @@ -1101,7 +1121,11 @@ func (c *WatchClient) extractPodContainersAttributes(pod *api_v1.Pod) PodContain
if !needContainerAttributes(c.Rules) {
return containers
}
if c.Rules.ContainerImageName || c.Rules.ContainerImageTag ||

enableStable := EnableStableAttributes.IsEnabled()
disableLegacy := DisableLegacyAttributes.IsEnabled()

if c.Rules.ContainerImageName || c.Rules.ContainerImageTag || c.Rules.ContainerImageTags ||
c.Rules.ServiceVersion || c.Rules.ServiceInstanceID {
specs := append(pod.Spec.Containers, pod.Spec.InitContainers...) //nolint:gocritic // appendAssign: append result not assigned to the same slice
for i := range specs {
Expand All @@ -1112,9 +1136,16 @@ func (c *WatchClient) extractPodContainersAttributes(pod *api_v1.Pod) PodContain
if c.Rules.ContainerImageName {
container.ImageName = imageRef.Repository
}
if c.Rules.ContainerImageTag {
// Legacy: container.image.tag (singular, string)
if c.Rules.ContainerImageTag && !disableLegacy {
container.ImageTag = imageRef.Tag
}
// Stable: container.image.tags (plural, array)
if c.Rules.ContainerImageTags && enableStable {
if imageRef.Tag != "" {
container.ImageTags = []string{imageRef.Tag}
}
}
if c.Rules.ServiceVersion {
serviceVersion, err := parseServiceVersionFromImage(spec.Image)
if err == nil {
Expand Down Expand Up @@ -1171,22 +1202,25 @@ func (c *WatchClient) extractPodContainersAttributes(pod *api_v1.Pod) PodContain
func (c *WatchClient) extractNamespaceAttributes(namespace *api_v1.Namespace) map[string]string {
tags := map[string]string{}

formatterLabel := K8sNamespaceLabelsKey
if AllowLabelsAnnotationsSingular.IsEnabled() {
formatterLabel = K8sNamespaceLabelKey
}
enableStable := EnableStableAttributes.IsEnabled()
disableLegacy := DisableLegacyAttributes.IsEnabled()

for _, r := range c.Rules.Labels {
r.extractFromNamespaceMetadata(namespace.Labels, tags, formatterLabel)
}

formatterAnnotation := K8sNamespaceAnnotationsKey
if AllowLabelsAnnotationsSingular.IsEnabled() {
formatterAnnotation = K8sNamespaceAnnotationKey
if !disableLegacy {
r.extractFromNamespaceMetadata(namespace.Labels, tags, K8sNamespaceLabelsKey)
}
if enableStable {
r.extractFromNamespaceMetadata(namespace.Labels, tags, K8sNamespaceLabelKey)
}
}

for _, r := range c.Rules.Annotations {
r.extractFromNamespaceMetadata(namespace.Annotations, tags, formatterAnnotation)
if !disableLegacy {
r.extractFromNamespaceMetadata(namespace.Annotations, tags, K8sNamespaceAnnotationsKey)
}
if enableStable {
r.extractFromNamespaceMetadata(namespace.Annotations, tags, K8sNamespaceAnnotationKey)
}
}

return tags
Expand All @@ -1195,22 +1229,25 @@ func (c *WatchClient) extractNamespaceAttributes(namespace *api_v1.Namespace) ma
func (c *WatchClient) extractNodeAttributes(node *api_v1.Node) map[string]string {
tags := map[string]string{}

formatterLabel := K8sNodeLabelsKey
if AllowLabelsAnnotationsSingular.IsEnabled() {
formatterLabel = K8sNodeLabelKey
}
enableStable := EnableStableAttributes.IsEnabled()
disableLegacy := DisableLegacyAttributes.IsEnabled()

for _, r := range c.Rules.Labels {
r.extractFromNodeMetadata(node.Labels, tags, formatterLabel)
}

formatterAnnotation := K8sNodeAnnotationsKey
if AllowLabelsAnnotationsSingular.IsEnabled() {
formatterAnnotation = K8sNodeAnnotationKey
if !disableLegacy {
r.extractFromNodeMetadata(node.Labels, tags, K8sNodeLabelsKey)
}
if enableStable {
r.extractFromNodeMetadata(node.Labels, tags, K8sNodeLabelKey)
}
}

for _, r := range c.Rules.Annotations {
r.extractFromNodeMetadata(node.Annotations, tags, formatterAnnotation)
if !disableLegacy {
r.extractFromNodeMetadata(node.Annotations, tags, K8sNodeAnnotationsKey)
}
if enableStable {
r.extractFromNodeMetadata(node.Annotations, tags, K8sNodeAnnotationKey)
}
}
return tags
}
Expand Down Expand Up @@ -1755,6 +1792,7 @@ func needContainerAttributes(rules ExtractionRules) bool {
return rules.ContainerImageName ||
rules.ContainerName ||
rules.ContainerImageTag ||
rules.ContainerImageTags ||
rules.ContainerImageRepoDigests ||
rules.ContainerID ||
rules.ServiceVersion ||
Expand Down
Loading
Loading