diff --git a/processor/resourcedetectionprocessor/internal/aws/eks/detector.go b/processor/resourcedetectionprocessor/internal/aws/eks/detector.go index 17331d162a37a..12aa0fbaf8983 100644 --- a/processor/resourcedetectionprocessor/internal/aws/eks/detector.go +++ b/processor/resourcedetectionprocessor/internal/aws/eks/detector.go @@ -43,6 +43,7 @@ type detectorUtils interface { getConfigMap(ctx context.Context, namespace string, name string) (map[string]string, error) getClusterName(ctx context.Context, logger *zap.Logger) string getClusterNameTagFromReservations([]*ec2.Reservation) string + getCloudAccountID(ctx context.Context, logger *zap.Logger) string } type eksDetectorUtils struct { @@ -87,6 +88,10 @@ func (d *detector) Detect(ctx context.Context) (resource pcommon.Resource, schem d.rb.SetCloudProvider(conventions.AttributeCloudProviderAWS) d.rb.SetCloudPlatform(conventions.AttributeCloudPlatformAWSEKS) + if d.ra.CloudAccountID.Enabled { + accountId := d.utils.getCloudAccountID(ctx, d.logger) + d.rb.SetCloudAccountID(accountId) + } if d.ra.K8sClusterName.Enabled { clusterName := d.utils.getClusterName(ctx, d.logger) @@ -194,3 +199,21 @@ func (e eksDetectorUtils) getClusterNameTagFromReservations(reservations []*ec2. return "" } + +func (e eksDetectorUtils) getCloudAccountID(ctx context.Context, logger *zap.Logger) string { + defaultErrorMessage := "Unable to get EKS cluster account ID" + sess, err := session.NewSession() + if err != nil { + logger.Warn(defaultErrorMessage, zap.Error(err)) + return "" + } + + ec2Svc := ec2metadata.New(sess) + instanceIdentityDocument, err := ec2Svc.GetInstanceIdentityDocumentWithContext(ctx) + if err != nil { + logger.Warn(defaultErrorMessage, zap.Error(err)) + return "" + } + + return instanceIdentityDocument.AccountID +} diff --git a/processor/resourcedetectionprocessor/internal/aws/eks/detector_test.go b/processor/resourcedetectionprocessor/internal/aws/eks/detector_test.go index e269f1bcbea6e..0528ca88691dd 100644 --- a/processor/resourcedetectionprocessor/internal/aws/eks/detector_test.go +++ b/processor/resourcedetectionprocessor/internal/aws/eks/detector_test.go @@ -19,7 +19,8 @@ import ( ) const ( - clusterName = "my-cluster" + clusterName = "my-cluster" + cloudAccountId = "cloud1234" ) type MockDetectorUtils struct { @@ -40,6 +41,10 @@ func (detectorUtils *MockDetectorUtils) getClusterNameTagFromReservations(_ []*e return clusterName } +func (detectorUtils *MockDetectorUtils) getCloudAccountID(_ context.Context, _ *zap.Logger) string { + return cloudAccountId +} + func TestNewDetector(t *testing.T) { dcfg := CreateDefaultConfig() detector, err := NewDetector(processortest.NewNopSettings(), dcfg) @@ -60,8 +65,9 @@ func TestEKS(t *testing.T) { require.NoError(t, err) assert.Equal(t, map[string]any{ - "cloud.provider": "aws", - "cloud.platform": "aws_eks", + "cloud.provider": "aws", + "cloud.platform": "aws_eks", + "cloud.account.id": "cloud1234", }, res.Attributes().AsRaw(), "Resource object returned is incorrect") } @@ -72,3 +78,57 @@ func TestNotEKS(t *testing.T) { require.NoError(t, err) assert.Equal(t, 0, r.Attributes().Len(), "Resource object should be empty") } + +func TestEKSResourceDetection_ForCloudAccountID(t *testing.T) { + tests := []struct { + name string + ra metadata.ResourceAttributesConfig + expectedOutput map[string]any + shouldError bool + }{ + { + name: "Detects CloudAccountID when enabled", + ra: metadata.ResourceAttributesConfig{ + CloudAccountID: metadata.ResourceAttributeConfig{Enabled: true}, + }, + expectedOutput: map[string]any{ + "cloud.account.id": "cloud1234", + }, + shouldError: false, + }, + { + name: "Does not detect CloudAccountID when disabled", + ra: metadata.ResourceAttributesConfig{ + CloudAccountID: metadata.ResourceAttributeConfig{Enabled: false}, + }, + expectedOutput: map[string]any{}, + shouldError: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + detectorUtils := new(MockDetectorUtils) + ctx := context.Background() + + t.Setenv("KUBERNETES_SERVICE_HOST", "localhost") + detectorUtils.On("getConfigMap", authConfigmapNS, authConfigmapName).Return(map[string]string{conventions.AttributeK8SClusterName: clusterName}, nil) + + eksResourceDetector := &detector{ + utils: detectorUtils, + err: nil, + ra: tt.ra, + rb: metadata.NewResourceBuilder(tt.ra), + } + res, _, err := eksResourceDetector.Detect(ctx) + + if tt.shouldError { + assert.Error(t, err) + return + } + + assert.NoError(t, err) + assert.Equal(t, tt.expectedOutput, res.Attributes().AsRaw()) + }) + } +} diff --git a/processor/resourcedetectionprocessor/internal/aws/eks/documentation.md b/processor/resourcedetectionprocessor/internal/aws/eks/documentation.md index 454e5488e5678..2ac83033fd094 100644 --- a/processor/resourcedetectionprocessor/internal/aws/eks/documentation.md +++ b/processor/resourcedetectionprocessor/internal/aws/eks/documentation.md @@ -8,6 +8,7 @@ | Name | Description | Values | Enabled | | ---- | ----------- | ------ | ------- | +| cloud.account.id | The cloud account id | Any Str | true | | cloud.platform | The cloud.platform | Any Str | true | | cloud.provider | The cloud.provider | Any Str | true | | k8s.cluster.name | The EKS cluster name. This attribute is currently only available when running on EC2 instances, and requires permission to run the EC2:DescribeInstances action. | Any Str | false | diff --git a/processor/resourcedetectionprocessor/internal/aws/eks/internal/metadata/generated_config.go b/processor/resourcedetectionprocessor/internal/aws/eks/internal/metadata/generated_config.go index fc9cd23ded420..9d1fe8b9f6211 100644 --- a/processor/resourcedetectionprocessor/internal/aws/eks/internal/metadata/generated_config.go +++ b/processor/resourcedetectionprocessor/internal/aws/eks/internal/metadata/generated_config.go @@ -27,6 +27,7 @@ func (rac *ResourceAttributeConfig) Unmarshal(parser *confmap.Conf) error { // ResourceAttributesConfig provides config for resourcedetectionprocessor/eks resource attributes. type ResourceAttributesConfig struct { + CloudAccountID ResourceAttributeConfig `mapstructure:"cloud.account.id"` CloudPlatform ResourceAttributeConfig `mapstructure:"cloud.platform"` CloudProvider ResourceAttributeConfig `mapstructure:"cloud.provider"` K8sClusterName ResourceAttributeConfig `mapstructure:"k8s.cluster.name"` @@ -34,6 +35,9 @@ type ResourceAttributesConfig struct { func DefaultResourceAttributesConfig() ResourceAttributesConfig { return ResourceAttributesConfig{ + CloudAccountID: ResourceAttributeConfig{ + Enabled: true, + }, CloudPlatform: ResourceAttributeConfig{ Enabled: true, }, diff --git a/processor/resourcedetectionprocessor/internal/aws/eks/internal/metadata/generated_config_test.go b/processor/resourcedetectionprocessor/internal/aws/eks/internal/metadata/generated_config_test.go index d7af18d45edae..942ec6dcf638e 100644 --- a/processor/resourcedetectionprocessor/internal/aws/eks/internal/metadata/generated_config_test.go +++ b/processor/resourcedetectionprocessor/internal/aws/eks/internal/metadata/generated_config_test.go @@ -24,6 +24,7 @@ func TestResourceAttributesConfig(t *testing.T) { { name: "all_set", want: ResourceAttributesConfig{ + CloudAccountID: ResourceAttributeConfig{Enabled: true}, CloudPlatform: ResourceAttributeConfig{Enabled: true}, CloudProvider: ResourceAttributeConfig{Enabled: true}, K8sClusterName: ResourceAttributeConfig{Enabled: true}, @@ -32,6 +33,7 @@ func TestResourceAttributesConfig(t *testing.T) { { name: "none_set", want: ResourceAttributesConfig{ + CloudAccountID: ResourceAttributeConfig{Enabled: false}, CloudPlatform: ResourceAttributeConfig{Enabled: false}, CloudProvider: ResourceAttributeConfig{Enabled: false}, K8sClusterName: ResourceAttributeConfig{Enabled: false}, diff --git a/processor/resourcedetectionprocessor/internal/aws/eks/internal/metadata/generated_resource.go b/processor/resourcedetectionprocessor/internal/aws/eks/internal/metadata/generated_resource.go index b4286d831a6b4..66e57035a6404 100644 --- a/processor/resourcedetectionprocessor/internal/aws/eks/internal/metadata/generated_resource.go +++ b/processor/resourcedetectionprocessor/internal/aws/eks/internal/metadata/generated_resource.go @@ -21,6 +21,13 @@ func NewResourceBuilder(rac ResourceAttributesConfig) *ResourceBuilder { } } +// SetCloudAccountID sets provided value as "cloud.account.id" attribute. +func (rb *ResourceBuilder) SetCloudAccountID(val string) { + if rb.config.CloudAccountID.Enabled { + rb.res.Attributes().PutStr("cloud.account.id", val) + } +} + // SetCloudPlatform sets provided value as "cloud.platform" attribute. func (rb *ResourceBuilder) SetCloudPlatform(val string) { if rb.config.CloudPlatform.Enabled { diff --git a/processor/resourcedetectionprocessor/internal/aws/eks/internal/metadata/generated_resource_test.go b/processor/resourcedetectionprocessor/internal/aws/eks/internal/metadata/generated_resource_test.go index 93a128c963ccb..c20fb41a27dcd 100644 --- a/processor/resourcedetectionprocessor/internal/aws/eks/internal/metadata/generated_resource_test.go +++ b/processor/resourcedetectionprocessor/internal/aws/eks/internal/metadata/generated_resource_test.go @@ -13,6 +13,7 @@ func TestResourceBuilder(t *testing.T) { t.Run(tt, func(t *testing.T) { cfg := loadResourceAttributesConfig(t, tt) rb := NewResourceBuilder(cfg) + rb.SetCloudAccountID("cloud.account.id-val") rb.SetCloudPlatform("cloud.platform-val") rb.SetCloudProvider("cloud.provider-val") rb.SetK8sClusterName("k8s.cluster.name-val") @@ -22,9 +23,9 @@ func TestResourceBuilder(t *testing.T) { switch tt { case "default": - assert.Equal(t, 2, res.Attributes().Len()) - case "all_set": assert.Equal(t, 3, res.Attributes().Len()) + case "all_set": + assert.Equal(t, 4, res.Attributes().Len()) case "none_set": assert.Equal(t, 0, res.Attributes().Len()) return @@ -32,7 +33,12 @@ func TestResourceBuilder(t *testing.T) { assert.Failf(t, "unexpected test case: %s", tt) } - val, ok := res.Attributes().Get("cloud.platform") + val, ok := res.Attributes().Get("cloud.account.id") + assert.True(t, ok) + if ok { + assert.EqualValues(t, "cloud.account.id-val", val.Str()) + } + val, ok = res.Attributes().Get("cloud.platform") assert.True(t, ok) if ok { assert.EqualValues(t, "cloud.platform-val", val.Str()) diff --git a/processor/resourcedetectionprocessor/internal/aws/eks/internal/metadata/testdata/config.yaml b/processor/resourcedetectionprocessor/internal/aws/eks/internal/metadata/testdata/config.yaml index 1b7d4c7eda41e..c573092fba4ea 100644 --- a/processor/resourcedetectionprocessor/internal/aws/eks/internal/metadata/testdata/config.yaml +++ b/processor/resourcedetectionprocessor/internal/aws/eks/internal/metadata/testdata/config.yaml @@ -1,6 +1,8 @@ default: all_set: resource_attributes: + cloud.account.id: + enabled: true cloud.platform: enabled: true cloud.provider: @@ -9,6 +11,8 @@ all_set: enabled: true none_set: resource_attributes: + cloud.account.id: + enabled: false cloud.platform: enabled: false cloud.provider: diff --git a/processor/resourcedetectionprocessor/internal/aws/eks/metadata.yaml b/processor/resourcedetectionprocessor/internal/aws/eks/metadata.yaml index 168b658c9e5f9..7919bc8e5a54f 100644 --- a/processor/resourcedetectionprocessor/internal/aws/eks/metadata.yaml +++ b/processor/resourcedetectionprocessor/internal/aws/eks/metadata.yaml @@ -11,6 +11,10 @@ resource_attributes: description: The cloud.platform type: string enabled: true + cloud.account.id: + description: The cloud account id + type: string + enabled: true k8s.cluster.name: 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. type: string