Skip to content

Commit 60430e1

Browse files
[processor/resourcedetection] Add k8s cluster name detection in EKS environment (#28649)
**Description:** This enhancement detects the k8s cluster name in EKS. The solution uses EC2 instance tags to determine the cluster name, which means it will only work on EC2 (as noted in documentation updates). Resolves #26794 --------- Co-authored-by: bryan-aguilar <[email protected]>
1 parent 1e8d4c0 commit 60430e1

File tree

10 files changed

+164
-11
lines changed

10 files changed

+164
-11
lines changed

.chloggen/eks_cluster_name.yaml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Use this changelog template to create an entry for release notes.
2+
3+
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
4+
change_type: enhancement
5+
6+
# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
7+
component: resourcedetectionprocessor
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: Add k8s cluster name detection when running in EKS
11+
12+
# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
13+
issues: [26794]
14+
15+
# (Optional) One or more lines of additional information to render under the primary note.
16+
# These lines will be padded with 2 spaces and then inserted directly into the document.
17+
# Use pipe (|) for multiline entries.
18+
subtext:
19+
20+
# If your change doesn't affect end users or the exported elements of any package,
21+
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
22+
# Optional: The change log or logs in which this entry should be included.
23+
# e.g. '[user]' or '[user, api]'
24+
# Include 'user' if the change is relevant to end users.
25+
# Include 'api' if there is a change to a library API.
26+
# Default: '[user]'
27+
change_logs: [user]

processor/resourcedetectionprocessor/README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,14 +318,18 @@ processors:
318318

319319
* cloud.provider ("aws")
320320
* cloud.platform ("aws_eks")
321+
* k8s.cluster.name
322+
323+
Note: The kubernetes cluster name is only available when running on EC2 instances, and requires permission to run the `EC2:DescribeInstances` [action](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeInstances.html).
324+
If you see an error with the message `context deadline exceeded`, please increase the timeout setting in your config.
321325

322326
Example:
323327

324328
```yaml
325329
processors:
326330
resourcedetection/eks:
327331
detectors: [env, eks]
328-
timeout: 2s
332+
timeout: 15s
329333
override: false
330334
```
331335

processor/resourcedetectionprocessor/internal/aws/eks/detector.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,12 @@ import (
77
"context"
88
"fmt"
99
"os"
10+
"strings"
1011

12+
"github.com/aws/aws-sdk-go/aws"
13+
"github.com/aws/aws-sdk-go/aws/ec2metadata"
14+
"github.com/aws/aws-sdk-go/aws/session"
15+
"github.com/aws/aws-sdk-go/service/ec2"
1116
"go.opentelemetry.io/collector/pdata/pcommon"
1217
"go.opentelemetry.io/collector/processor"
1318
conventions "go.opentelemetry.io/collector/semconv/v1.6.1"
@@ -28,10 +33,16 @@ const (
2833
kubernetesServiceHostEnvVar = "KUBERNETES_SERVICE_HOST"
2934
authConfigmapNS = "kube-system"
3035
authConfigmapName = "aws-auth"
36+
37+
clusterNameAwsEksTag = "aws:eks:cluster-name"
38+
clusterNameEksTag = "eks:cluster-name"
39+
kubernetesClusterNameTag = "kubernetes.io/cluster/"
3140
)
3241

3342
type detectorUtils interface {
3443
getConfigMap(ctx context.Context, namespace string, name string) (map[string]string, error)
44+
getClusterName(ctx context.Context, logger *zap.Logger) string
45+
getClusterNameTagFromReservations([]*ec2.Reservation) string
3546
}
3647

3748
type eksDetectorUtils struct {
@@ -43,6 +54,7 @@ type detector struct {
4354
utils detectorUtils
4455
logger *zap.Logger
4556
err error
57+
ra metadata.ResourceAttributesConfig
4658
rb *metadata.ResourceBuilder
4759
}
4860

@@ -54,10 +66,12 @@ var _ detectorUtils = (*eksDetectorUtils)(nil)
5466
func NewDetector(set processor.CreateSettings, dcfg internal.DetectorConfig) (internal.Detector, error) {
5567
cfg := dcfg.(Config)
5668
utils, err := newK8sDetectorUtils()
69+
5770
return &detector{
5871
utils: utils,
5972
logger: set.Logger,
6073
err: err,
74+
ra: cfg.ResourceAttributes,
6175
rb: metadata.NewResourceBuilder(cfg.ResourceAttributes),
6276
}, nil
6377
}
@@ -74,6 +88,11 @@ func (d *detector) Detect(ctx context.Context) (resource pcommon.Resource, schem
7488
d.rb.SetCloudProvider(conventions.AttributeCloudProviderAWS)
7589
d.rb.SetCloudPlatform(conventions.AttributeCloudPlatformAWSEKS)
7690

91+
if d.ra.K8sClusterName.Enabled {
92+
clusterName := d.utils.getClusterName(ctx, d.logger)
93+
d.rb.SetK8sClusterName(clusterName)
94+
}
95+
7796
return d.rb.Emit(), conventions.SchemaURL, nil
7897
}
7998

@@ -114,3 +133,64 @@ func (e eksDetectorUtils) getConfigMap(ctx context.Context, namespace string, na
114133
}
115134
return cm.Data, nil
116135
}
136+
137+
func (e eksDetectorUtils) getClusterName(ctx context.Context, logger *zap.Logger) string {
138+
defaultErrorMessage := "Unable to get EKS cluster name"
139+
sess, err := session.NewSession()
140+
if err != nil {
141+
logger.Warn(defaultErrorMessage, zap.Error(err))
142+
return ""
143+
}
144+
145+
ec2Svc := ec2metadata.New(sess)
146+
region, err := ec2Svc.Region()
147+
if err != nil {
148+
logger.Warn(defaultErrorMessage, zap.Error(err))
149+
return ""
150+
}
151+
152+
svc := ec2.New(sess, aws.NewConfig().WithRegion(region))
153+
instanceIdentityDocument, err := ec2Svc.GetInstanceIdentityDocumentWithContext(ctx)
154+
if err != nil {
155+
logger.Warn(defaultErrorMessage, zap.Error(err))
156+
return ""
157+
}
158+
159+
instances, err := svc.DescribeInstances(&ec2.DescribeInstancesInput{
160+
InstanceIds: []*string{
161+
aws.String(instanceIdentityDocument.InstanceID),
162+
},
163+
})
164+
if err != nil {
165+
logger.Warn(defaultErrorMessage, zap.Error(err))
166+
return ""
167+
}
168+
169+
clusterName := e.getClusterNameTagFromReservations(instances.Reservations)
170+
if len(clusterName) == 0 {
171+
logger.Warn("Failed to detect EKS cluster name. No tag for cluster name found on EC2 instance")
172+
return ""
173+
}
174+
175+
return clusterName
176+
}
177+
178+
func (e eksDetectorUtils) getClusterNameTagFromReservations(reservations []*ec2.Reservation) string {
179+
for _, reservation := range reservations {
180+
for _, instance := range reservation.Instances {
181+
for _, tag := range instance.Tags {
182+
if tag.Key == nil {
183+
continue
184+
}
185+
186+
if *tag.Key == clusterNameAwsEksTag || *tag.Key == clusterNameEksTag {
187+
return *tag.Value
188+
} else if strings.HasPrefix(*tag.Key, kubernetesClusterNameTag) {
189+
return strings.TrimPrefix(*tag.Key, kubernetesClusterNameTag)
190+
}
191+
}
192+
}
193+
}
194+
195+
return ""
196+
}

processor/resourcedetectionprocessor/internal/aws/eks/detector_test.go

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,21 @@ import (
77
"context"
88
"testing"
99

10+
"github.com/aws/aws-sdk-go/service/ec2"
1011
"github.com/stretchr/testify/assert"
1112
"github.com/stretchr/testify/mock"
1213
"github.com/stretchr/testify/require"
1314
"go.opentelemetry.io/collector/processor/processortest"
15+
conventions "go.opentelemetry.io/collector/semconv/v1.6.1"
1416
"go.uber.org/zap"
1517

1618
"github.com/open-telemetry/opentelemetry-collector-contrib/processor/resourcedetectionprocessor/internal/aws/eks/internal/metadata"
1719
)
1820

21+
const (
22+
clusterName = "my-cluster"
23+
)
24+
1925
type MockDetectorUtils struct {
2026
mock.Mock
2127
}
@@ -25,6 +31,15 @@ func (detectorUtils *MockDetectorUtils) getConfigMap(_ context.Context, namespac
2531
return args.Get(0).(map[string]string), args.Error(1)
2632
}
2733

34+
func (detectorUtils *MockDetectorUtils) getClusterName(_ context.Context, _ *zap.Logger) string {
35+
var reservations []*ec2.Reservation
36+
return detectorUtils.getClusterNameTagFromReservations(reservations)
37+
}
38+
39+
func (detectorUtils *MockDetectorUtils) getClusterNameTagFromReservations(_ []*ec2.Reservation) string {
40+
return clusterName
41+
}
42+
2843
func TestNewDetector(t *testing.T) {
2944
dcfg := CreateDefaultConfig()
3045
detector, err := NewDetector(processortest.NewNopCreateSettings(), dcfg)
@@ -38,9 +53,9 @@ func TestEKS(t *testing.T) {
3853
ctx := context.Background()
3954

4055
t.Setenv("KUBERNETES_SERVICE_HOST", "localhost")
41-
detectorUtils.On("getConfigMap", authConfigmapNS, authConfigmapName).Return(map[string]string{"cluster.name": "my-cluster"}, nil)
56+
detectorUtils.On("getConfigMap", authConfigmapNS, authConfigmapName).Return(map[string]string{conventions.AttributeK8SClusterName: clusterName}, nil)
4257
// Call EKS Resource detector to detect resources
43-
eksResourceDetector := &detector{utils: detectorUtils, err: nil, rb: metadata.NewResourceBuilder(metadata.DefaultResourceAttributesConfig())}
58+
eksResourceDetector := &detector{utils: detectorUtils, err: nil, ra: metadata.DefaultResourceAttributesConfig(), rb: metadata.NewResourceBuilder(metadata.DefaultResourceAttributesConfig())}
4459
res, _, err := eksResourceDetector.Detect(ctx)
4560
require.NoError(t, err)
4661

processor/resourcedetectionprocessor/internal/aws/eks/internal/metadata/generated_config.go

Lines changed: 6 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

processor/resourcedetectionprocessor/internal/aws/eks/internal/metadata/generated_config_test.go

Lines changed: 6 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

processor/resourcedetectionprocessor/internal/aws/eks/internal/metadata/generated_resource.go

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

processor/resourcedetectionprocessor/internal/aws/eks/internal/metadata/generated_resource_test.go

Lines changed: 7 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

processor/resourcedetectionprocessor/internal/aws/eks/internal/metadata/testdata/config.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,13 @@ all_set:
55
enabled: true
66
cloud.provider:
77
enabled: true
8+
k8s.cluster.name:
9+
enabled: true
810
none_set:
911
resource_attributes:
1012
cloud.platform:
1113
enabled: false
1214
cloud.provider:
1315
enabled: false
16+
k8s.cluster.name:
17+
enabled: false

processor/resourcedetectionprocessor/internal/aws/eks/metadata.yaml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,8 @@ resource_attributes:
1010
cloud.platform:
1111
description: The cloud.platform
1212
type: string
13-
enabled: true
13+
enabled: true
14+
k8s.cluster.name:
15+
description: The EKS cluster name. This attribute is currently only available when running on EC2 instances, and requires permission to run the EC2:DescribeInstances action.
16+
type: string
17+
enabled: false

0 commit comments

Comments
 (0)