Skip to content
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
16 changes: 16 additions & 0 deletions .chloggen/signalfxexporter-strip-k8s-label-prefix.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
change_type: enhancement

component: exporter/signalfx

note: Add `dimension_client::strip_k8s_label_prefix` option to strip `k8s.<resource>.label.` prefix from dimension property updates.

issues: [47491]

subtext: |
The k8s cluster receiver now emits Kubernetes resource labels in entity events with the
`k8s.<resource>.label.` prefix per OTel semantic conventions (e.g. `k8s.pod.label.app`).
Comment thread
dmitryax marked this conversation as resolved.
When `strip_k8s_label_prefix: true` (the default), the SignalFx exporter strips this prefix
when forwarding labels as dimension properties, preserving the existing SignalFx behavior (e.g. `app`).
Set `strip_k8s_label_prefix: false` to disable stripping and receive the full prefixed keys.

change_logs: [user]
1 change: 1 addition & 0 deletions exporter/signalfxexporter/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ type DimensionClientConfig struct {
IdleConnTimeout time.Duration `mapstructure:"idle_conn_timeout"`
Timeout time.Duration `mapstructure:"timeout"`
DropTags bool `mapstructure:"drop_tags"`
StripK8sLabelPrefix bool `mapstructure:"strip_k8s_label_prefix"`
}

func (cfg *Config) getMetricTranslator(done chan struct{}) (*translation.MetricTranslator, error) {
Expand Down
2 changes: 2 additions & 0 deletions exporter/signalfxexporter/config.schema.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ $defs:
send_delay:
type: string
format: duration
strip_k8s_label_prefix:
type: boolean
timeout:
type: string
format: duration
Expand Down
2 changes: 2 additions & 0 deletions exporter/signalfxexporter/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ func TestLoadConfig(t *testing.T) {
IdleConnTimeout: 30 * time.Second,
Timeout: 10 * time.Second,
DropTags: false,
StripK8sLabelPrefix: true,
},
ExcludeMetrics: nil,
IncludeMetrics: nil,
Expand Down Expand Up @@ -165,6 +166,7 @@ func TestLoadConfig(t *testing.T) {
IdleConnTimeout: 2 * time.Hour,
Timeout: 20 * time.Second,
DropTags: false,
StripK8sLabelPrefix: true,
},
DefaultProperties: map[string]string{
"foo": "bar",
Expand Down
1 change: 1 addition & 0 deletions exporter/signalfxexporter/exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ func (se *signalfxExporter) startDimensionClient(ctx context.Context) error {
IdleConnTimeout: se.config.DimensionClient.IdleConnTimeout,
Timeout: se.config.DimensionClient.Timeout,
DropTags: se.config.DimensionClient.DropTags,
StripK8sLabelPrefix: se.config.DimensionClient.StripK8sLabelPrefix,
})
dimClient.Start()
se.dimClient = dimClient
Expand Down
1 change: 1 addition & 0 deletions exporter/signalfxexporter/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ func createDefaultConfig() component.Config {
MaxIdleConnsPerHost: defaultDimMaxIdleConnsPerHost,
IdleConnTimeout: idleConnTimeout,
Timeout: timeout,
StripK8sLabelPrefix: true,
},
}
}
Expand Down
7 changes: 7 additions & 0 deletions exporter/signalfxexporter/internal/dimensions/dimclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ type DimensionClient struct {
ExcludeProperties []dpfilters.PropertyFilter
// dropTags specifies whether tags should be omitted or not. Default value is false.
dropTags bool
// stripK8sLabelPrefix controls whether the `k8s.<resource>.label.` prefix is stripped
// from Kubernetes resource label keys before sending them as dimension property updates.
// This applies to all resource types except k8s.service, which already sends labels with
// the prefix. Default is true.
stripK8sLabelPrefix bool
}

type queuedDimension struct {
Expand All @@ -87,6 +92,7 @@ type DimensionClientOptions struct {
IdleConnTimeout time.Duration
Timeout time.Duration
DropTags bool
StripK8sLabelPrefix bool
}

// NewDimensionClient returns a new client
Expand Down Expand Up @@ -125,6 +131,7 @@ func NewDimensionClient(options DimensionClientOptions) *DimensionClient {
DefaultProperties: options.DefaultProperties,
ExcludeProperties: options.ExcludeProperties,
dropTags: options.DropTags,
stripK8sLabelPrefix: options.StripK8sLabelPrefix,
}
}

Expand Down
23 changes: 16 additions & 7 deletions exporter/signalfxexporter/internal/dimensions/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package dimensions // import "github.com/open-telemetry/opentelemetry-collector-

import (
"fmt"
"regexp"
"strings"
"unicode"

Expand All @@ -22,9 +23,10 @@ func getDimensionUpdateFromMetadata(
defaults map[string]string,
metadata metadata.MetadataUpdate,
nonAlphanumericDimChars string,
stripK8sLabelPrefix bool,
) *DimensionUpdate {
skipSanitization := metadata.ResourceIDKey == "k8s.service.uid"
properties, tags := getPropertiesAndTags(defaults, metadata, skipSanitization)
properties, tags := getPropertiesAndTags(defaults, metadata, skipSanitization, stripK8sLabelPrefix)

return &DimensionUpdate{
Name: FilterKeyChars(metadata.ResourceIDKey, nonAlphanumericDimChars),
Expand All @@ -39,14 +41,21 @@ const (
sfxK8sServicePrefix = "kubernetes_service_"
)

func sanitizeProperty(property string) string {
var oTelK8sLabelRe = regexp.MustCompile(`^k8s\.[^.]+\.label\.(.+)$`)

func sanitizeProperty(property string, stripK8sLabelPrefix bool) string {
if strings.HasPrefix(property, oTelK8sServicePrefix) {
return strings.Replace(property, oTelK8sServicePrefix, sfxK8sServicePrefix, 1)
}
if stripK8sLabelPrefix {
if m := oTelK8sLabelRe.FindStringSubmatch(property); m != nil {
return m[1]
}
}
return property
}

func getPropertiesAndTags(defaults map[string]string, kmu metadata.MetadataUpdate, skipSanitization bool) (map[string]*string, map[string]bool) {
func getPropertiesAndTags(defaults map[string]string, kmu metadata.MetadataUpdate, skipSanitization, stripK8sLabelPrefix bool) (map[string]*string, map[string]bool) {
properties := map[string]*string{}
tags := map[string]bool{}

Expand All @@ -57,7 +66,7 @@ func getPropertiesAndTags(defaults map[string]string, kmu metadata.MetadataUpdat
for label, val := range kmu.MetadataToAdd {
key := label
if !skipSanitization {
key = sanitizeProperty(label)
key = sanitizeProperty(label, stripK8sLabelPrefix)
}
if key == "" {
continue
Expand All @@ -74,7 +83,7 @@ func getPropertiesAndTags(defaults map[string]string, kmu metadata.MetadataUpdat
for label, val := range kmu.MetadataToRemove {
key := label
if !skipSanitization {
key = sanitizeProperty(label)
key = sanitizeProperty(label, stripK8sLabelPrefix)
}
if key == "" {
continue
Expand All @@ -90,7 +99,7 @@ func getPropertiesAndTags(defaults map[string]string, kmu metadata.MetadataUpdat
for label, val := range kmu.MetadataToUpdate {
key := label
if !skipSanitization {
key = sanitizeProperty(label)
key = sanitizeProperty(label, stripK8sLabelPrefix)
}
if key == "" {
continue
Expand All @@ -112,7 +121,7 @@ func getPropertiesAndTags(defaults map[string]string, kmu metadata.MetadataUpdat
func (dc *DimensionClient) PushMetadata(metadata []*metadata.MetadataUpdate) error {
var errs error
for _, m := range metadata {
dimensionUpdate := getDimensionUpdateFromMetadata(dc.DefaultProperties, *m, dc.nonAlphanumericDimChars)
dimensionUpdate := getDimensionUpdateFromMetadata(dc.DefaultProperties, *m, dc.nonAlphanumericDimChars, dc.stripK8sLabelPrefix)

if dimensionUpdate.Name == "" || dimensionUpdate.Value == "" {
return fmt.Errorf("dimensionUpdate %v is missing Name or value, cannot send", dimensionUpdate)
Expand Down
64 changes: 61 additions & 3 deletions exporter/signalfxexporter/internal/dimensions/metadata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ import (

func TestGetDimensionUpdateFromMetadata(t *testing.T) {
type args struct {
defaults map[string]string
metadata metadata.MetadataUpdate
defaults map[string]string
metadata metadata.MetadataUpdate
stripK8sLabelPrefix bool
}
tests := []struct {
name string
Expand Down Expand Up @@ -117,6 +118,63 @@ func TestGetDimensionUpdateFromMetadata(t *testing.T) {
},
},
},
{
"Test k8s resource label prefix is stripped",
args{
stripK8sLabelPrefix: true,
metadata: metadata.MetadataUpdate{
ResourceIDKey: "k8s.pod.uid",
ResourceID: "pod-123",
MetadataDelta: metadata.MetadataDelta{
MetadataToAdd: map[string]string{
"k8s.pod.label.app": "my-app",
"k8s.node.label.topology.kubernetes.io/zone": "",
"k8s.deployment.label.version": "v1",
},
MetadataToRemove: map[string]string{
"k8s.pod.label.old-label": "old-value",
},
},
},
},
&DimensionUpdate{
Name: "k8s.pod.uid",
Value: "pod-123",
Properties: getMapToPointers(map[string]string{
"app": "my-app",
"version": "v1",
"old-label": "",
}),
Tags: map[string]bool{
"topology.kubernetes.io/zone": true,
},
},
},
{
"Test k8s service label prefix is NOT stripped even when stripK8sLabelPrefix is true",
args{
stripK8sLabelPrefix: true,
metadata: metadata.MetadataUpdate{
ResourceIDKey: "k8s.service.uid",
ResourceID: "svc-123",
MetadataDelta: metadata.MetadataDelta{
MetadataToAdd: map[string]string{
"k8s.service.label.app": "my-app",
"k8s.service.label.version": "v1",
},
},
},
},
&DimensionUpdate{
Name: "k8s.service.uid",
Value: "svc-123",
Properties: getMapToPointers(map[string]string{
"k8s.service.label.app": "my-app",
"k8s.service.label.version": "v1",
}),
Tags: map[string]bool{},
},
},
{
"Test with k8s service properties",
args{
Expand Down Expand Up @@ -269,7 +327,7 @@ func TestGetDimensionUpdateFromMetadata(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equal(t, tt.want, getDimensionUpdateFromMetadata(tt.args.defaults, tt.args.metadata, "-_."))
assert.Equal(t, tt.want, getDimensionUpdateFromMetadata(tt.args.defaults, tt.args.metadata, "-_.", tt.args.stripK8sLabelPrefix))
})
}
}
Expand Down
Loading