Skip to content

Commit

Permalink
[processor/resourcedetection]: Populate "aws.ecs.task.id" property fo…
Browse files Browse the repository at this point in the history
…r ECS Tasks (#8274) (#29602)

**Description:**
The `resourcedetection` processor now populates `aws.ecs.task.id`
property (in addition to other `aws.ecs.task.*`
properties). This simplifies configuration of `awsemfexporter`, which
internally searches for `aws.ecs.task.id`
property when using `TaskId` placeholder in `loggroup` / `logstream`
name template.

**Link to tracking Issue:**
#8274

**Testing:** ECS Task ARNs come in two versions. In the old one, the
last part of the ARN contains only the `task/<task-id>`. In the new one,
it contains `task/cluster-name/task-id`. Implementation and Unit Tests
have been added to handle both cases.

**Documentation:** `README.md` now also mentions `aws.ecs.task.id` as
inferred property for ECS.
  • Loading branch information
mkielar authored Dec 12, 2023
1 parent b6b6a6f commit 22f9b4e
Show file tree
Hide file tree
Showing 10 changed files with 152 additions and 11 deletions.
30 changes: 30 additions & 0 deletions .chloggen/resourcedetection-ecs-task-id.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Use this changelog template to create an entry for release notes.

# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: enhancement

# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
component: resourcedetectionprocessor

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: Add "aws.ecs.task.id" attribute

# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
issues: [8274]

# (Optional) One or more lines of additional information to render under the primary note.
# These lines will be padded with 2 spaces and then inserted directly into the document.
# Use pipe (|) for multiline entries.
subtext: |
Resourcedetectionprocessor now exports "aws.ecs.task.id" attribute, in addition to "aws.ecs.task.arn".
This allows exporters like "awsemfexporter" to automatically pick up that attribute and make it available
in templating (e.g. to use in CloudWatch log stream name).
# If your change doesn't affect end users or the exported elements of any package,
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
# Optional: The change log or logs in which this entry should be included.
# e.g. '[user]' or '[user, api]'
# Include 'user' if the change is relevant to end users.
# Include 'api' if there is a change to a library API.
# Default: '[user]'
change_logs: []
1 change: 1 addition & 0 deletions processor/resourcedetectionprocessor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@ Queries the [Task Metadata Endpoint](https://docs.aws.amazon.com/AmazonECS/lates
* aws.ecs.cluster.arn
* aws.ecs.task.arn
* aws.ecs.task.family
* aws.ecs.task.id
* aws.ecs.task.revision
* aws.ecs.launchtype (V4 only)
* aws.log.group.names (V4 only)
Expand Down
24 changes: 19 additions & 5 deletions processor/resourcedetectionprocessor/internal/aws/ecs/ecs.go
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func (d *Detector) Detect(context.Context) (resource pcommon.Resource, schemaURL
d.rb.SetAwsEcsTaskFamily(tmdeResp.Family)
d.rb.SetAwsEcsTaskRevision(tmdeResp.Revision)

region, account := parseRegionAndAccount(tmdeResp.TaskARN)
region, account, taskID := parseTaskARN(tmdeResp.TaskARN)
if account != "" {
d.rb.SetCloudAccountID(account)
}
Expand All @@ -75,6 +75,10 @@ func (d *Detector) Detect(context.Context) (resource pcommon.Resource, schemaURL
d.rb.SetCloudRegion(region)
}

if taskID != "" {
d.rb.SetAwsEcsTaskID(taskID)
}

// TMDE returns the cluster short name or ARN, so we need to construct the ARN if necessary
d.rb.SetAwsEcsClusterArn(constructClusterArn(tmdeResp.Cluster, region, account))

Expand Down Expand Up @@ -110,15 +114,25 @@ func constructClusterArn(cluster, region, account string) string {
return fmt.Sprintf("arn:aws:ecs:%s:%s:cluster/%s", region, account, cluster)
}

// Parses the AWS Account ID and AWS Region from a task ARN
// Parses ECS Task ARN into subcomponents according to its spec
// See: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-account-settings.html#ecs-resource-ids
func parseRegionAndAccount(taskARN string) (region string, account string) {
func parseTaskARN(taskARN string) (region string, account string, taskID string) {
parts := strings.Split(taskARN, ":")
if len(parts) >= 5 {
return parts[3], parts[4]
region := parts[3]
account := parts[4]

// ECS Task ARNs come in two versions. In the old one, the last part of the ARN contains
// only the "task/<task-id>". In the new one, it contains "task/cluster-name/task-id".
// This handles both cases.
taskInfo := parts[5]
taskInfoParts := strings.Split(taskInfo, "/")
taskID := taskInfoParts[len(taskInfoParts)-1]

return region, account, taskID
}

return "", ""
return "", "", ""
}

// Filter out non-normal containers, our own container since we assume the collector is run as a sidecar,
Expand Down
77 changes: 73 additions & 4 deletions processor/resourcedetectionprocessor/internal/aws/ecs/ecs_test.go
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package ecs

import (
"context"
"fmt"
"testing"

"github.com/stretchr/testify/assert"
Expand All @@ -17,7 +18,8 @@ import (
)

type mockMetaDataProvider struct {
isV4 bool
isV4 bool
taskArnVersion int `default:"1"`
}

var _ ecsutil.MetadataProvider = (*mockMetaDataProvider)(nil)
Expand All @@ -26,9 +28,20 @@ func (md *mockMetaDataProvider) FetchTaskMetadata() (*ecsutil.TaskMetadata, erro
c := createTestContainer(md.isV4)
c.DockerID = "05281997" // Simulate one "application" and one "collector" container
cs := []ecsutil.ContainerMetadata{createTestContainer(md.isV4), c}

var taskARN string
switch md.taskArnVersion {
case 1:
taskARN = "arn:aws:ecs:us-west-2:123456789123:task/123"
case 2:
taskARN = "arn:aws:ecs:us-west-2:123456789123:task/my-cluster/123"
default:
return nil, fmt.Errorf("%s: %d", "Unsupported ECS TaskARN Spec Version", md.taskArnVersion)
}

tmd := &ecsutil.TaskMetadata{
Cluster: "my-cluster",
TaskARN: "arn:aws:ecs:us-west-2:123456789123:task/123",
TaskARN: taskARN,
Family: "family",
AvailabilityZone: "us-west-2a",
Revision: "26",
Expand Down Expand Up @@ -84,6 +97,7 @@ func Test_ecsFiltersInvalidContainers(t *testing.T) {
AwsEcsClusterArn: metadata.ResourceAttributeConfig{Enabled: true},
AwsEcsLaunchtype: metadata.ResourceAttributeConfig{Enabled: true},
AwsEcsTaskArn: metadata.ResourceAttributeConfig{Enabled: true},
AwsEcsTaskID: metadata.ResourceAttributeConfig{Enabled: true},
AwsEcsTaskFamily: metadata.ResourceAttributeConfig{Enabled: true},
AwsEcsTaskRevision: metadata.ResourceAttributeConfig{Enabled: true},
AwsLogGroupArns: metadata.ResourceAttributeConfig{Enabled: true},
Expand All @@ -109,6 +123,36 @@ func Test_ecsDetectV4(t *testing.T) {
attr.PutStr("cloud.platform", "aws_ecs")
attr.PutStr("aws.ecs.cluster.arn", "arn:aws:ecs:us-west-2:123456789123:cluster/my-cluster")
attr.PutStr("aws.ecs.task.arn", "arn:aws:ecs:us-west-2:123456789123:task/123")
attr.PutStr("aws.ecs.task.id", "123")
attr.PutStr("aws.ecs.task.family", "family")
attr.PutStr("aws.ecs.task.revision", "26")
attr.PutStr("cloud.region", "us-west-2")
attr.PutStr("cloud.availability_zone", "us-west-2a")
attr.PutStr("cloud.account.id", "123456789123")
attr.PutStr("aws.ecs.launchtype", "ec2")
attr.PutEmptySlice("aws.log.group.names").AppendEmpty().SetStr("group")
attr.PutEmptySlice("aws.log.group.arns").AppendEmpty().SetStr("arn:aws:logs:us-east-1:123456789123:log-group:group")
attr.PutEmptySlice("aws.log.stream.names").AppendEmpty().SetStr("stream")
attr.PutEmptySlice("aws.log.stream.arns").AppendEmpty().SetStr("arn:aws:logs:us-east-1:123456789123:log-group:group:log-stream:stream")

d := Detector{provider: &mockMetaDataProvider{isV4: true, taskArnVersion: 1}, rb: metadata.NewResourceBuilder(metadata.DefaultResourceAttributesConfig())}
got, _, err := d.Detect(context.TODO())

assert.Nil(t, err)
assert.NotNil(t, got)
assert.Equal(t, want.Attributes().AsRaw(), got.Attributes().AsRaw())
}

func Test_ecsDetectV4WithTaskArnVersion2(t *testing.T) {
t.Setenv(endpoints.TaskMetadataEndpointV4EnvVar, "endpoint")

want := pcommon.NewResource()
attr := want.Attributes()
attr.PutStr("cloud.provider", "aws")
attr.PutStr("cloud.platform", "aws_ecs")
attr.PutStr("aws.ecs.cluster.arn", "arn:aws:ecs:us-west-2:123456789123:cluster/my-cluster")
attr.PutStr("aws.ecs.task.arn", "arn:aws:ecs:us-west-2:123456789123:task/my-cluster/123")
attr.PutStr("aws.ecs.task.id", "123")
attr.PutStr("aws.ecs.task.family", "family")
attr.PutStr("aws.ecs.task.revision", "26")
attr.PutStr("cloud.region", "us-west-2")
Expand All @@ -120,7 +164,7 @@ func Test_ecsDetectV4(t *testing.T) {
attr.PutEmptySlice("aws.log.stream.names").AppendEmpty().SetStr("stream")
attr.PutEmptySlice("aws.log.stream.arns").AppendEmpty().SetStr("arn:aws:logs:us-east-1:123456789123:log-group:group:log-stream:stream")

d := Detector{provider: &mockMetaDataProvider{isV4: true}, rb: metadata.NewResourceBuilder(metadata.DefaultResourceAttributesConfig())}
d := Detector{provider: &mockMetaDataProvider{isV4: true, taskArnVersion: 2}, rb: metadata.NewResourceBuilder(metadata.DefaultResourceAttributesConfig())}
got, _, err := d.Detect(context.TODO())

assert.Nil(t, err)
Expand All @@ -137,13 +181,38 @@ func Test_ecsDetectV3(t *testing.T) {
attr.PutStr("cloud.platform", "aws_ecs")
attr.PutStr("aws.ecs.cluster.arn", "arn:aws:ecs:us-west-2:123456789123:cluster/my-cluster")
attr.PutStr("aws.ecs.task.arn", "arn:aws:ecs:us-west-2:123456789123:task/123")
attr.PutStr("aws.ecs.task.id", "123")
attr.PutStr("aws.ecs.task.family", "family")
attr.PutStr("aws.ecs.task.revision", "26")
attr.PutStr("cloud.region", "us-west-2")
attr.PutStr("cloud.availability_zone", "us-west-2a")
attr.PutStr("cloud.account.id", "123456789123")

d := Detector{provider: &mockMetaDataProvider{isV4: false, taskArnVersion: 1}, rb: metadata.NewResourceBuilder(metadata.DefaultResourceAttributesConfig())}
got, _, err := d.Detect(context.TODO())

assert.Nil(t, err)
assert.NotNil(t, got)
assert.Equal(t, want.Attributes().AsRaw(), got.Attributes().AsRaw())
}

func Test_ecsDetectV3WithTaskArnVersion2(t *testing.T) {
t.Setenv(endpoints.TaskMetadataEndpointV3EnvVar, "endpoint")

want := pcommon.NewResource()
attr := want.Attributes()
attr.PutStr("cloud.provider", "aws")
attr.PutStr("cloud.platform", "aws_ecs")
attr.PutStr("aws.ecs.cluster.arn", "arn:aws:ecs:us-west-2:123456789123:cluster/my-cluster")
attr.PutStr("aws.ecs.task.arn", "arn:aws:ecs:us-west-2:123456789123:task/my-cluster/123")
attr.PutStr("aws.ecs.task.id", "123")
attr.PutStr("aws.ecs.task.family", "family")
attr.PutStr("aws.ecs.task.revision", "26")
attr.PutStr("cloud.region", "us-west-2")
attr.PutStr("cloud.availability_zone", "us-west-2a")
attr.PutStr("cloud.account.id", "123456789123")

d := Detector{provider: &mockMetaDataProvider{isV4: false}, rb: metadata.NewResourceBuilder(metadata.DefaultResourceAttributesConfig())}
d := Detector{provider: &mockMetaDataProvider{isV4: false, taskArnVersion: 2}, rb: metadata.NewResourceBuilder(metadata.DefaultResourceAttributesConfig())}
got, _, err := d.Detect(context.TODO())

assert.Nil(t, err)
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ all_set:
enabled: true
aws.ecs.task.family:
enabled: true
aws.ecs.task.id:
enabled: true
aws.ecs.task.revision:
enabled: true
aws.log.group.arns:
Expand Down Expand Up @@ -39,6 +41,8 @@ none_set:
enabled: false
aws.ecs.task.family:
enabled: false
aws.ecs.task.id:
enabled: false
aws.ecs.task.revision:
enabled: false
aws.log.group.arns:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ resource_attributes:
description: The aws.ecs.task.family
type: string
enabled: true
aws.ecs.task.id:
description: The aws.ecs.task.id
type: string
enabled: true
aws.ecs.task.revision:
description: The aws.ecs.task.revision
type: string
Expand Down

0 comments on commit 22f9b4e

Please sign in to comment.