From 9d8be4e30f2986eae2026cb603828654d494862f Mon Sep 17 00:00:00 2001 From: Michele Mancioppi Date: Sat, 6 Aug 2022 22:45:35 +0200 Subject: [PATCH 1/8] Implemented aws.ecs.* resource attributes in go.opentelemetry.io/detectors/aws/ecs --- CHANGELOG.md | 4 + detectors/aws/ecs/ecs.go | 49 +++++++++- detectors/aws/ecs/ecs_test.go | 55 ++++++++++- detectors/aws/ecs/go.mod | 1 + detectors/aws/ecs/go.sum | 2 + .../metadatav4-response-container.json | 44 +++++++++ .../testdata/metadatav4-response-task.json | 94 +++++++++++++++++++ 7 files changed, 240 insertions(+), 9 deletions(-) create mode 100644 detectors/aws/ecs/testdata/metadatav4-response-container.json create mode 100644 detectors/aws/ecs/testdata/metadatav4-response-task.json diff --git a/CHANGELOG.md b/CHANGELOG.md index d1ce0928d4b..fe9be2e03dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased] +### Added + +- Implemented retrieving the [`aws.ecs.*` resource attributes](https://opentelemetry.io/docs/reference/specification/resource/semantic_conventions/cloud_provider/aws/ecs/) in `go.opentelemetry.io/detectors/aws/ecs` based on the ECS Metadata v4 endpoint. + ## [1.11.1/0.36.4/0.5.2] ### Added diff --git a/detectors/aws/ecs/ecs.go b/detectors/aws/ecs/ecs.go index 236288a6feb..454cca0b7fe 100644 --- a/detectors/aws/ecs/ecs.go +++ b/detectors/aws/ecs/ecs.go @@ -17,9 +17,13 @@ package ecs // import "go.opentelemetry.io/contrib/detectors/aws/ecs" import ( "context" "errors" + "fmt" + "net/http" "os" "strings" + ecsmetadata "github.com/brunoscheufler/aws-ecs-metadata-go" + "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/sdk/resource" semconv "go.opentelemetry.io/otel/semconv/v1.12.0" @@ -35,10 +39,12 @@ const ( ) var ( - empty = resource.Empty() - errCannotReadContainerID = errors.New("failed to read container ID from cGroupFile") - errCannotReadContainerName = errors.New("failed to read hostname") - errCannotReadCGroupFile = errors.New("ECS resource detector failed to read cGroupFile") + empty = resource.Empty() + errCannotReadContainerID = errors.New("failed to read container ID from cGroupFile") + errCannotReadContainerName = errors.New("failed to read hostname") + errCannotReadCGroupFile = errors.New("ECS resource detector failed to read cGroupFile") + errCannotRetrieveMetadataV4 = errors.New("ECS resource detector failed to retrieve metadata from the ECS Metada v4 container endpoint") + errCannotRetrieveMetadataV4Task = errors.New("ECS resource detector failed to retrieve metadata from the ECS Metada v4 task endpoint") ) // Create interface for methods needing to be mocked. @@ -63,7 +69,9 @@ var _ resource.Detector = (*resourceDetector)(nil) // NewResourceDetector returns a resource detector that will detect AWS ECS resources. func NewResourceDetector() resource.Detector { - return &resourceDetector{utils: ecsDetectorUtils{}} + return &resourceDetector{ + utils: ecsDetectorUtils{}, + } } // Detect finds associated resources when running on ECS environment. @@ -89,6 +97,37 @@ func (detector *resourceDetector) Detect(ctx context.Context) (*resource.Resourc semconv.ContainerIDKey.String(containerID), } + if len(metadataURIV4) > 0 { + containerMetadata, err := ecsmetadata.GetContainerV4(ctx, &http.Client{}) + if err != nil { + return empty, err + } + attributes = append( + attributes, + semconv.AWSECSContainerARNKey.String(containerMetadata.ContainerARN), + ) + + taskMetadata, err := ecsmetadata.GetTaskV4(ctx, &http.Client{}) + if err != nil { + return empty, err + } + + clusterArn := taskMetadata.Cluster + if !strings.HasPrefix(clusterArn, "arn:") { + baseArn := containerMetadata.ContainerARN[:strings.LastIndex(containerMetadata.ContainerARN, ":")] + clusterArn = fmt.Sprintf("%s:cluster/%s", baseArn, clusterArn) + } + + attributes = append( + attributes, + semconv.AWSECSClusterARNKey.String(clusterArn), + semconv.AWSECSLaunchtypeKey.String(taskMetadata.LaunchType), + semconv.AWSECSTaskARNKey.String(taskMetadata.TaskARN), + semconv.AWSECSTaskFamilyKey.String(taskMetadata.Family), + semconv.AWSECSTaskRevisionKey.String(taskMetadata.Revision), + ) + } + return resource.NewWithAttributes(semconv.SchemaURL, attributes...), nil } diff --git a/detectors/aws/ecs/ecs_test.go b/detectors/aws/ecs/ecs_test.go index 71b2c1e5451..ce1a47cfa64 100644 --- a/detectors/aws/ecs/ecs_test.go +++ b/detectors/aws/ecs/ecs_test.go @@ -16,7 +16,10 @@ package ecs import ( "context" + http "net/http" + "net/http/httptest" "os" + "strings" "testing" "github.com/stretchr/testify/assert" @@ -42,11 +45,10 @@ func (detectorUtils *MockDetectorUtils) getContainerName() (string, error) { return args.String(0), args.Error(1) } -// successfully return resource when process is running on Amazon ECS environment. -func TestDetect(t *testing.T) { +// succesfully return resource when process is running on Amazon ECS environment with no Metadata v4. +func TestDetectV3(t *testing.T) { os.Clearenv() _ = os.Setenv(metadataV3EnvVar, "3") - _ = os.Setenv(metadataV4EnvVar, "4") detectorUtils := new(MockDetectorUtils) @@ -63,7 +65,52 @@ func TestDetect(t *testing.T) { detector := &resourceDetector{utils: detectorUtils} res, _ := detector.Detect(context.Background()) - assert.Equal(t, res, expectedResource, "Resource returned is incorrect") + assert.Equal(t, expectedResource, res, "Resource returned is incorrect") +} + +//succesfully return resource when process is running on Amazon ECS environment with Metadata v4. +func TestDetectV4(t *testing.T) { + testServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { + if strings.HasSuffix(req.URL.String(), "/task") { + content, err := os.ReadFile("testdata/metadatav4-response-task.json") + if err == nil { + res.Write(content) + } + } else { + content, err := os.ReadFile("testdata/metadatav4-response-container.json") + if err == nil { + res.Write(content) + } + } + })) + defer func() { testServer.Close() }() + + os.Clearenv() + _ = os.Setenv(metadataV3EnvVar, "3") + _ = os.Setenv(metadataV4EnvVar, testServer.URL) + + detectorUtils := new(MockDetectorUtils) + + detectorUtils.On("getContainerName").Return("container-Name", nil) + detectorUtils.On("getContainerID").Return("0123456789A", nil) + + attributes := []attribute.KeyValue{ + semconv.CloudProviderAWS, + semconv.CloudPlatformAWSECS, + semconv.ContainerNameKey.String("container-Name"), + semconv.ContainerIDKey.String("0123456789A"), + semconv.AWSECSContainerARNKey.String("arn:aws:ecs:us-west-2:111122223333:container/0206b271-b33f-47ab-86c6-a0ba208a70a9"), + semconv.AWSECSClusterARNKey.String("arn:aws:ecs:us-west-2:111122223333:cluster/default"), + semconv.AWSECSLaunchtypeKey.String("EC2"), + semconv.AWSECSTaskARNKey.String("arn:aws:ecs:us-west-2:111122223333:task/default/158d1c8083dd49d6b527399fd6414f5c"), + semconv.AWSECSTaskFamilyKey.String("curltest"), + semconv.AWSECSTaskRevisionKey.String("26"), + } + expectedResource := resource.NewWithAttributes(semconv.SchemaURL, attributes...) + detector := &resourceDetector{utils: detectorUtils} + res, _ := detector.Detect(context.Background()) + + assert.Equal(t, expectedResource, res, "Resource returned is incorrect") } // returns empty resource when detector cannot read container ID. diff --git a/detectors/aws/ecs/go.mod b/detectors/aws/ecs/go.mod index c555ce80eb7..cdd5a8d4f73 100644 --- a/detectors/aws/ecs/go.mod +++ b/detectors/aws/ecs/go.mod @@ -3,6 +3,7 @@ module go.opentelemetry.io/contrib/detectors/aws/ecs go 1.18 require ( + github.com/brunoscheufler/aws-ecs-metadata-go v0.0.0-20220806202253-1e93a1d357d6 github.com/stretchr/testify v1.8.0 go.opentelemetry.io/otel v1.11.1 go.opentelemetry.io/otel/sdk v1.11.1 diff --git a/detectors/aws/ecs/go.sum b/detectors/aws/ecs/go.sum index 3ef536da06b..091f04f5250 100644 --- a/detectors/aws/ecs/go.sum +++ b/detectors/aws/ecs/go.sum @@ -1,3 +1,5 @@ +github.com/brunoscheufler/aws-ecs-metadata-go v0.0.0-20220806202253-1e93a1d357d6 h1:Izqf3e8tWDmFCnSRdmtej33tKPWlPYIvPPXqcI9qimM= +github.com/brunoscheufler/aws-ecs-metadata-go v0.0.0-20220806202253-1e93a1d357d6/go.mod h1:5BBxNAuQFkDjY3l9UHlJ8NnnM6NEi0bocZR2xc3xGxU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/detectors/aws/ecs/testdata/metadatav4-response-container.json b/detectors/aws/ecs/testdata/metadatav4-response-container.json new file mode 100644 index 00000000000..b43c2b0d7dc --- /dev/null +++ b/detectors/aws/ecs/testdata/metadatav4-response-container.json @@ -0,0 +1,44 @@ +{ + "DockerId": "ea32192c8553fbff06c9340478a2ff089b2bb5646fb718b4ee206641c9086d66", + "Name": "curl", + "DockerName": "ecs-curltest-24-curl-cca48e8dcadd97805600", + "Image": "111122223333.dkr.ecr.us-west-2.amazonaws.com/curltest:latest", + "ImageID": "sha256:d691691e9652791a60114e67b365688d20d19940dde7c4736ea30e660d8d3553", + "Labels": { + "com.amazonaws.ecs.cluster": "default", + "com.amazonaws.ecs.container-name": "curl", + "com.amazonaws.ecs.task-arn": "arn:aws:ecs:us-west-2:111122223333:task/default/8f03e41243824aea923aca126495f665", + "com.amazonaws.ecs.task-definition-family": "curltest", + "com.amazonaws.ecs.task-definition-version": "24" + }, + "DesiredStatus": "RUNNING", + "KnownStatus": "RUNNING", + "Limits": { + "CPU": 10, + "Memory": 128 + }, + "CreatedAt": "2020-10-02T00:15:07.620912337Z", + "StartedAt": "2020-10-02T00:15:08.062559351Z", + "Type": "NORMAL", + "LogDriver": "awslogs", + "LogOptions": { + "awslogs-create-group": "true", + "awslogs-group": "/ecs/metadata", + "awslogs-region": "us-west-2", + "awslogs-stream": "ecs/curl/8f03e41243824aea923aca126495f665" + }, + "ContainerARN": "arn:aws:ecs:us-west-2:111122223333:container/0206b271-b33f-47ab-86c6-a0ba208a70a9", + "Networks": [ + { + "NetworkMode": "awsvpc", + "IPv4Addresses": [ + "10.0.2.100" + ], + "AttachmentIndex": 0, + "MACAddress": "0e:9e:32:c7:48:85", + "IPv4SubnetCIDRBlock": "10.0.2.0/24", + "PrivateDNSName": "ip-10-0-2-100.us-west-2.compute.internal", + "SubnetGatewayIpv4Address": "10.0.2.1/24" + } + ] +} diff --git a/detectors/aws/ecs/testdata/metadatav4-response-task.json b/detectors/aws/ecs/testdata/metadatav4-response-task.json new file mode 100644 index 00000000000..101efe02148 --- /dev/null +++ b/detectors/aws/ecs/testdata/metadatav4-response-task.json @@ -0,0 +1,94 @@ +{ + "Cluster": "default", + "TaskARN": "arn:aws:ecs:us-west-2:111122223333:task/default/158d1c8083dd49d6b527399fd6414f5c", + "Family": "curltest", + "Revision": "26", + "DesiredStatus": "RUNNING", + "KnownStatus": "RUNNING", + "PullStartedAt": "2020-10-02T00:43:06.202617438Z", + "PullStoppedAt": "2020-10-02T00:43:06.31288465Z", + "AvailabilityZone": "us-west-2d", + "LaunchType": "EC2", + "Containers": [ + { + "DockerId": "598cba581fe3f939459eaba1e071d5c93bb2c49b7d1ba7db6bb19deeb70d8e38", + "Name": "~internal~ecs~pause", + "DockerName": "ecs-curltest-26-internalecspause-e292d586b6f9dade4a00", + "Image": "amazon/amazon-ecs-pause:0.1.0", + "ImageID": "", + "Labels": { + "com.amazonaws.ecs.cluster": "default", + "com.amazonaws.ecs.container-name": "~internal~ecs~pause", + "com.amazonaws.ecs.task-arn": "arn:aws:ecs:us-west-2:111122223333:task/default/158d1c8083dd49d6b527399fd6414f5c", + "com.amazonaws.ecs.task-definition-family": "curltest", + "com.amazonaws.ecs.task-definition-version": "26" + }, + "DesiredStatus": "RESOURCES_PROVISIONED", + "KnownStatus": "RESOURCES_PROVISIONED", + "Limits": { + "CPU": 0, + "Memory": 0 + }, + "CreatedAt": "2020-10-02T00:43:05.602352471Z", + "StartedAt": "2020-10-02T00:43:06.076707576Z", + "Type": "CNI_PAUSE", + "Networks": [ + { + "NetworkMode": "awsvpc", + "IPv4Addresses": [ + "10.0.2.61" + ], + "AttachmentIndex": 0, + "MACAddress": "0e:10:e2:01:bd:91", + "IPv4SubnetCIDRBlock": "10.0.2.0/24", + "PrivateDNSName": "ip-10-0-2-61.us-west-2.compute.internal", + "SubnetGatewayIpv4Address": "10.0.2.1/24" + } + ] + }, + { + "DockerId": "ee08638adaaf009d78c248913f629e38299471d45fe7dc944d1039077e3424ca", + "Name": "curl", + "DockerName": "ecs-curltest-26-curl-a0e7dba5aca6d8cb2e00", + "Image": "111122223333.dkr.ecr.us-west-2.amazonaws.com/curltest:latest", + "ImageID": "sha256:d691691e9652791a60114e67b365688d20d19940dde7c4736ea30e660d8d3553", + "Labels": { + "com.amazonaws.ecs.cluster": "default", + "com.amazonaws.ecs.container-name": "curl", + "com.amazonaws.ecs.task-arn": "arn:aws:ecs:us-west-2:111122223333:task/default/158d1c8083dd49d6b527399fd6414f5c", + "com.amazonaws.ecs.task-definition-family": "curltest", + "com.amazonaws.ecs.task-definition-version": "26" + }, + "DesiredStatus": "RUNNING", + "KnownStatus": "RUNNING", + "Limits": { + "CPU": 10, + "Memory": 128 + }, + "CreatedAt": "2020-10-02T00:43:06.326590752Z", + "StartedAt": "2020-10-02T00:43:06.767535449Z", + "Type": "NORMAL", + "LogDriver": "awslogs", + "LogOptions": { + "awslogs-create-group": "true", + "awslogs-group": "/ecs/metadata", + "awslogs-region": "us-west-2", + "awslogs-stream": "ecs/curl/158d1c8083dd49d6b527399fd6414f5c" + }, + "ContainerARN": "arn:aws:ecs:us-west-2:111122223333:container/abb51bdd-11b4-467f-8f6c-adcfe1fe059d", + "Networks": [ + { + "NetworkMode": "awsvpc", + "IPv4Addresses": [ + "10.0.2.61" + ], + "AttachmentIndex": 0, + "MACAddress": "0e:10:e2:01:bd:91", + "IPv4SubnetCIDRBlock": "10.0.2.0/24", + "PrivateDNSName": "ip-10-0-2-61.us-west-2.compute.internal", + "SubnetGatewayIpv4Address": "10.0.2.1/24" + } + ] + } + ] +} From 6a1b2749dcf99d3ec963180f1cb7dd6ee43b7c5b Mon Sep 17 00:00:00 2001 From: Michele Mancioppi Date: Sun, 7 Aug 2022 18:54:20 +0200 Subject: [PATCH 2/8] Update detectors/aws/ecs/ecs_test.go Co-authored-by: Chester Cheung --- detectors/aws/ecs/ecs_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/detectors/aws/ecs/ecs_test.go b/detectors/aws/ecs/ecs_test.go index ce1a47cfa64..f0ff50dbef4 100644 --- a/detectors/aws/ecs/ecs_test.go +++ b/detectors/aws/ecs/ecs_test.go @@ -45,7 +45,8 @@ func (detectorUtils *MockDetectorUtils) getContainerName() (string, error) { return args.String(0), args.Error(1) } -// succesfully return resource when process is running on Amazon ECS environment with no Metadata v4. +// succesfully returns resource when process is running on Amazon ECS environment +// with no Metadata v4. func TestDetectV3(t *testing.T) { os.Clearenv() _ = os.Setenv(metadataV3EnvVar, "3") From 8b5d348c3f1b0af3fa9229ca6119ad5c2c2c1a20 Mon Sep 17 00:00:00 2001 From: Michele Mancioppi Date: Sun, 7 Aug 2022 18:54:32 +0200 Subject: [PATCH 3/8] Update detectors/aws/ecs/ecs_test.go Co-authored-by: Chester Cheung --- detectors/aws/ecs/ecs_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/detectors/aws/ecs/ecs_test.go b/detectors/aws/ecs/ecs_test.go index f0ff50dbef4..5cdcc8beaee 100644 --- a/detectors/aws/ecs/ecs_test.go +++ b/detectors/aws/ecs/ecs_test.go @@ -69,7 +69,8 @@ func TestDetectV3(t *testing.T) { assert.Equal(t, expectedResource, res, "Resource returned is incorrect") } -//succesfully return resource when process is running on Amazon ECS environment with Metadata v4. +// succesfully returns resource when process is running on Amazon ECS environment +// with Metadata v4. func TestDetectV4(t *testing.T) { testServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { if strings.HasSuffix(req.URL.String(), "/task") { From a5bde89208d0a8cd27575dd6efecb35cc627ab3f Mon Sep 17 00:00:00 2001 From: Michele Mancioppi Date: Tue, 9 Aug 2022 22:11:15 +0200 Subject: [PATCH 4/8] fix: lower-case the value of aws.ecs.launchtype --- detectors/aws/ecs/ecs.go | 2 +- detectors/aws/ecs/ecs_test.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/detectors/aws/ecs/ecs.go b/detectors/aws/ecs/ecs.go index 454cca0b7fe..b6fd5acfe31 100644 --- a/detectors/aws/ecs/ecs.go +++ b/detectors/aws/ecs/ecs.go @@ -121,7 +121,7 @@ func (detector *resourceDetector) Detect(ctx context.Context) (*resource.Resourc attributes = append( attributes, semconv.AWSECSClusterARNKey.String(clusterArn), - semconv.AWSECSLaunchtypeKey.String(taskMetadata.LaunchType), + semconv.AWSECSLaunchtypeKey.String(strings.ToLower(taskMetadata.LaunchType)), semconv.AWSECSTaskARNKey.String(taskMetadata.TaskARN), semconv.AWSECSTaskFamilyKey.String(taskMetadata.Family), semconv.AWSECSTaskRevisionKey.String(taskMetadata.Revision), diff --git a/detectors/aws/ecs/ecs_test.go b/detectors/aws/ecs/ecs_test.go index 5cdcc8beaee..07b64a0050e 100644 --- a/detectors/aws/ecs/ecs_test.go +++ b/detectors/aws/ecs/ecs_test.go @@ -69,7 +69,7 @@ func TestDetectV3(t *testing.T) { assert.Equal(t, expectedResource, res, "Resource returned is incorrect") } -// succesfully returns resource when process is running on Amazon ECS environment +// succesfully returns resource when process is running on Amazon ECS environment // with Metadata v4. func TestDetectV4(t *testing.T) { testServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { @@ -103,7 +103,7 @@ func TestDetectV4(t *testing.T) { semconv.ContainerIDKey.String("0123456789A"), semconv.AWSECSContainerARNKey.String("arn:aws:ecs:us-west-2:111122223333:container/0206b271-b33f-47ab-86c6-a0ba208a70a9"), semconv.AWSECSClusterARNKey.String("arn:aws:ecs:us-west-2:111122223333:cluster/default"), - semconv.AWSECSLaunchtypeKey.String("EC2"), + semconv.AWSECSLaunchtypeKey.String("ec2"), semconv.AWSECSTaskARNKey.String("arn:aws:ecs:us-west-2:111122223333:task/default/158d1c8083dd49d6b527399fd6414f5c"), semconv.AWSECSTaskFamilyKey.String("curltest"), semconv.AWSECSTaskRevisionKey.String("26"), From 28bc9005dc6f2427887cce586602743464f051f9 Mon Sep 17 00:00:00 2001 From: Michele Mancioppi Date: Tue, 9 Aug 2022 22:08:37 +0200 Subject: [PATCH 5/8] Add aws.logs.* support, remove spurious /aws prefix from log group --- detectors/aws/ecs/ecs.go | 59 +++++++++++++++++++++++++++++++---- detectors/aws/ecs/ecs_test.go | 16 +++++++++- detectors/aws/ecs/go.mod | 2 +- detectors/aws/ecs/go.sum | 26 ++++++++------- 4 files changed, 84 insertions(+), 19 deletions(-) diff --git a/detectors/aws/ecs/ecs.go b/detectors/aws/ecs/ecs.go index b6fd5acfe31..e73dcf83e80 100644 --- a/detectors/aws/ecs/ecs.go +++ b/detectors/aws/ecs/ecs.go @@ -20,6 +20,7 @@ import ( "fmt" "net/http" "os" + "regexp" "strings" ecsmetadata "github.com/brunoscheufler/aws-ecs-metadata-go" @@ -39,12 +40,16 @@ const ( ) var ( - empty = resource.Empty() - errCannotReadContainerID = errors.New("failed to read container ID from cGroupFile") - errCannotReadContainerName = errors.New("failed to read hostname") - errCannotReadCGroupFile = errors.New("ECS resource detector failed to read cGroupFile") - errCannotRetrieveMetadataV4 = errors.New("ECS resource detector failed to retrieve metadata from the ECS Metada v4 container endpoint") - errCannotRetrieveMetadataV4Task = errors.New("ECS resource detector failed to retrieve metadata from the ECS Metada v4 task endpoint") + empty = resource.Empty() + errCannotReadContainerID = errors.New("failed to read container ID from cGroupFile") + errCannotReadContainerName = errors.New("failed to read hostname") + errCannotReadCGroupFile = errors.New("ECS resource detector failed to read cGroupFile") + errCannotRetrieveMetadataV4 = errors.New("ECS resource detector failed to retrieve metadata from the ECS Metadata v4 container endpoint") + errCannotRetrieveMetadataV4Task = errors.New("ECS resource detector failed to retrieve metadata from the ECS Metadata v4 task endpoint") + errCannotRetrieveLogsGroupMetadataV4 = errors.New("The ECS Metadata v4 did not return a AwsLogGroup name") + errCannotRetrieveLogsStreamMetadataV4 = errors.New("The ECS Metadata v4 did not return a AwsLogStream name") + errCannotParseAwsRegionMetadataV4 = errors.New("Cannot parse the AWS region our of the Container ARN returned by the ECS Metadata v4 container endpoint") + errCannotParseAwsAccountMetadataV4 = errors.New("Cannot parse the AWS account our of the Container ARN returned by the ECS Metadata v4 container endpoint") ) // Create interface for methods needing to be mocked. @@ -118,6 +123,15 @@ func (detector *resourceDetector) Detect(ctx context.Context) (*resource.Resourc clusterArn = fmt.Sprintf("%s:cluster/%s", baseArn, clusterArn) } + logAttributes, err := detector.getLogsAttributes(containerMetadata) + if err != nil { + return empty, err + } + + if len(logAttributes) > 0 { + attributes = append(attributes, logAttributes...) + } + attributes = append( attributes, semconv.AWSECSClusterARNKey.String(clusterArn), @@ -131,6 +145,39 @@ func (detector *resourceDetector) Detect(ctx context.Context) (*resource.Resourc return resource.NewWithAttributes(semconv.SchemaURL, attributes...), nil } +func (detector *resourceDetector) getLogsAttributes(metadata *ecsmetadata.ContainerMetadataV4) ([]attribute.KeyValue, error) { + if metadata.LogDriver != "awslogs" { + return []attribute.KeyValue{}, nil + } + + logsOptions := metadata.LogOptions + + if len(logsOptions.AwsLogsGroup) < 1 { + return nil, errCannotRetrieveLogsGroupMetadataV4 + } + + if len(logsOptions.AwsLogsStream) < 1 { + return nil, errCannotRetrieveLogsStreamMetadataV4 + } + + containerArn := metadata.ContainerARN + logsRegion := logsOptions.AwsRegion + if len(logsRegion) < 1 { + r := regexp.MustCompile(`arn:aws:ecs:([^:]+):.*`) + logsRegion = r.FindStringSubmatch(containerArn)[1] + } + + r := regexp.MustCompile(`arn:aws:ecs:[^:]+:([^:]+):.*`) + awsAccount := r.FindStringSubmatch(containerArn)[1] + + return []attribute.KeyValue{ + semconv.AWSLogGroupNamesKey.String(logsOptions.AwsLogsGroup), + semconv.AWSLogGroupARNsKey.String(fmt.Sprintf("arn:aws:logs:%s:%s:log-group:%s:*", logsRegion, awsAccount, logsOptions.AwsLogsGroup)), + semconv.AWSLogStreamNamesKey.String(logsOptions.AwsLogsStream), + semconv.AWSLogStreamARNsKey.String(fmt.Sprintf("arn:aws:logs:%s:%s:log-group:%s:log-stream:%s", logsRegion, awsAccount, logsOptions.AwsLogsGroup, logsOptions.AwsLogsStream)), + }, nil +} + // returns docker container ID from default c group path. func (ecsUtils ecsDetectorUtils) getContainerID() (string, error) { fileData, err := os.ReadFile(defaultCgroupPath) diff --git a/detectors/aws/ecs/ecs_test.go b/detectors/aws/ecs/ecs_test.go index 07b64a0050e..b0095d1791b 100644 --- a/detectors/aws/ecs/ecs_test.go +++ b/detectors/aws/ecs/ecs_test.go @@ -16,6 +16,7 @@ package ecs import ( "context" + "encoding/json" http "net/http" "net/http/httptest" "os" @@ -25,6 +26,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" + ecsmetadata "github.com/brunoscheufler/aws-ecs-metadata-go" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/sdk/resource" semconv "go.opentelemetry.io/otel/semconv/v1.12.0" @@ -107,11 +109,16 @@ func TestDetectV4(t *testing.T) { semconv.AWSECSTaskARNKey.String("arn:aws:ecs:us-west-2:111122223333:task/default/158d1c8083dd49d6b527399fd6414f5c"), semconv.AWSECSTaskFamilyKey.String("curltest"), semconv.AWSECSTaskRevisionKey.String("26"), + semconv.AWSLogGroupNamesKey.String("/ecs/metadata"), + semconv.AWSLogGroupARNsKey.String("arn:aws:logs:us-west-2:111122223333:log-group:/ecs/metadata:*"), + semconv.AWSLogStreamNamesKey.String("ecs/curl/8f03e41243824aea923aca126495f665"), + semconv.AWSLogStreamARNsKey.String("arn:aws:logs:us-west-2:111122223333:log-group:/ecs/metadata:log-stream:ecs/curl/8f03e41243824aea923aca126495f665"), } expectedResource := resource.NewWithAttributes(semconv.SchemaURL, attributes...) detector := &resourceDetector{utils: detectorUtils} - res, _ := detector.Detect(context.Background()) + res, err := detector.Detect(context.Background()) + assert.Equal(t, err, nil, "Detector should not file") assert.Equal(t, expectedResource, res, "Resource returned is incorrect") } @@ -149,6 +156,13 @@ func TestDetectCannotReadContainerName(t *testing.T) { assert.Equal(t, 0, len(res.Attributes())) } +func TestDetectV4Parse(t *testing.T) { + content, _ := os.ReadFile("testdata/metadatav4-response-task.json") + + taskMetadata := &ecsmetadata.TaskMetadataV4{} + json.Unmarshal(content, taskMetadata) +} + // returns empty resource when process is not running ECS. func TestReturnsIfNoEnvVars(t *testing.T) { os.Clearenv() diff --git a/detectors/aws/ecs/go.mod b/detectors/aws/ecs/go.mod index cdd5a8d4f73..53cc4a2c3eb 100644 --- a/detectors/aws/ecs/go.mod +++ b/detectors/aws/ecs/go.mod @@ -3,13 +3,13 @@ module go.opentelemetry.io/contrib/detectors/aws/ecs go 1.18 require ( - github.com/brunoscheufler/aws-ecs-metadata-go v0.0.0-20220806202253-1e93a1d357d6 github.com/stretchr/testify v1.8.0 go.opentelemetry.io/otel v1.11.1 go.opentelemetry.io/otel/sdk v1.11.1 ) require ( + github.com/brunoscheufler/aws-ecs-metadata-go v0.0.0-20220812150832-b6b31c6eeeaf // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-logr/logr v1.2.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect diff --git a/detectors/aws/ecs/go.sum b/detectors/aws/ecs/go.sum index 091f04f5250..b6e8c2d4bfe 100644 --- a/detectors/aws/ecs/go.sum +++ b/detectors/aws/ecs/go.sum @@ -1,5 +1,7 @@ -github.com/brunoscheufler/aws-ecs-metadata-go v0.0.0-20220806202253-1e93a1d357d6 h1:Izqf3e8tWDmFCnSRdmtej33tKPWlPYIvPPXqcI9qimM= -github.com/brunoscheufler/aws-ecs-metadata-go v0.0.0-20220806202253-1e93a1d357d6/go.mod h1:5BBxNAuQFkDjY3l9UHlJ8NnnM6NEi0bocZR2xc3xGxU= +github.com/brunoscheufler/aws-ecs-metadata-go v0.0.0-20220809142905-71ffda5b3a7b h1:sDlCP3q7DzAycTPuhV2U08iCoOwSO6SV4qOBlm012Vs= +github.com/brunoscheufler/aws-ecs-metadata-go v0.0.0-20220809142905-71ffda5b3a7b/go.mod h1:5BBxNAuQFkDjY3l9UHlJ8NnnM6NEi0bocZR2xc3xGxU= +github.com/brunoscheufler/aws-ecs-metadata-go v0.0.0-20220812150832-b6b31c6eeeaf h1:WCnJxXZXx9c8gwz598wvdqmu+YTzB9wx2X1OovK3Le8= +github.com/brunoscheufler/aws-ecs-metadata-go v0.0.0-20220812150832-b6b31c6eeeaf/go.mod h1:CeKhh8xSs3WZAc50xABMxu+FlfAAd5PNumo7NfOv7EE= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -8,7 +10,8 @@ github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -17,14 +20,15 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -go.opentelemetry.io/otel v1.11.1 h1:4WLLAmcfkmDk2ukNXJyq3/kiz/3UzCaYq6PskJsaou4= -go.opentelemetry.io/otel v1.11.1/go.mod h1:1nNhXBbWSD0nsL38H6btgnFN2k4i0sNLHNNMZMSbUGE= -go.opentelemetry.io/otel/sdk v1.11.1 h1:F7KmQgoHljhUuJyA+9BiU+EkJfyX5nVVF4wyzWZpKxs= -go.opentelemetry.io/otel/sdk v1.11.1/go.mod h1:/l3FE4SupHJ12TduVjUkZtlfFqDCQJlOlithYrdktys= -go.opentelemetry.io/otel/trace v1.11.1 h1:ofxdnzsNrGBYXbP7t7zpUK281+go5rF7dvdIZXF8gdQ= -go.opentelemetry.io/otel/trace v1.11.1/go.mod h1:f/Q9G7vzk5u91PhbmKbg1Qn0rzH1LJ4vbPHFGkTPtOk= -golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 h1:h+EGohizhe9XlX18rfpa8k8RAc5XyaeamM+0VHRd4lc= -golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +go.opentelemetry.io/otel v1.9.0 h1:8WZNQFIB2a71LnANS9JeyidJKKGOOremcUtb/OtHISw= +go.opentelemetry.io/otel v1.9.0/go.mod h1:np4EoPGzoPs3O67xUVNoPPcmSvsfOxNlNA4F4AC+0Eo= +go.opentelemetry.io/otel/sdk v1.9.0 h1:LNXp1vrr83fNXTHgU8eO89mhzxb/bbWAsHG6fNf3qWo= +go.opentelemetry.io/otel/sdk v1.9.0/go.mod h1:AEZc8nt5bd2F7BC24J5R0mrjYnpEgYHyTcM/vrSple4= +go.opentelemetry.io/otel/trace v1.9.0 h1:oZaCNJUjWcg60VXWee8lJKlqhPbXAPB51URuR47pQYc= +go.opentelemetry.io/otel/trace v1.9.0/go.mod h1:2737Q0MuG8q1uILYm2YYVkAyLtOofiTNGg6VODnOiPo= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20220808155132-1c4a2a72c664 h1:v1W7bwXHsnLLloWYTVEdvGvA7BHMeBYsPcF0GLDxIRs= +golang.org/x/sys v0.0.0-20220808155132-1c4a2a72c664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From e518c4052a7839ccf637707c80658611efa84543 Mon Sep 17 00:00:00 2001 From: Michele Mancioppi Date: Tue, 16 Aug 2022 16:16:01 +0200 Subject: [PATCH 6/8] Add tests for V4 on Fargate launch type --- detectors/aws/ecs/ecs_test.go | 59 +++++++++++++- detectors/aws/ecs/go.sum | 2 - ...=> metadatav4-response-container-ec2.json} | 0 ...metadatav4-response-container-fargate.json | 50 ++++++++++++ ...json => metadatav4-response-task-ec2.json} | 0 .../metadatav4-response-task-fargate.json | 77 +++++++++++++++++++ 6 files changed, 182 insertions(+), 6 deletions(-) rename detectors/aws/ecs/testdata/{metadatav4-response-container.json => metadatav4-response-container-ec2.json} (100%) create mode 100644 detectors/aws/ecs/testdata/metadatav4-response-container-fargate.json rename detectors/aws/ecs/testdata/{metadatav4-response-task.json => metadatav4-response-task-ec2.json} (100%) create mode 100644 detectors/aws/ecs/testdata/metadatav4-response-task-fargate.json diff --git a/detectors/aws/ecs/ecs_test.go b/detectors/aws/ecs/ecs_test.go index b0095d1791b..e43c2e42091 100644 --- a/detectors/aws/ecs/ecs_test.go +++ b/detectors/aws/ecs/ecs_test.go @@ -72,16 +72,16 @@ func TestDetectV3(t *testing.T) { } // succesfully returns resource when process is running on Amazon ECS environment -// with Metadata v4. -func TestDetectV4(t *testing.T) { +// with Metadata v4 with the EC2 Launch type. +func TestDetectV4LaunchTypeEc2(t *testing.T) { testServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { if strings.HasSuffix(req.URL.String(), "/task") { - content, err := os.ReadFile("testdata/metadatav4-response-task.json") + content, err := os.ReadFile("testdata/metadatav4-response-task-ec2.json") if err == nil { res.Write(content) } } else { - content, err := os.ReadFile("testdata/metadatav4-response-container.json") + content, err := os.ReadFile("testdata/metadatav4-response-container-ec2.json") if err == nil { res.Write(content) } @@ -122,6 +122,57 @@ func TestDetectV4(t *testing.T) { assert.Equal(t, expectedResource, res, "Resource returned is incorrect") } +// succesfully returns resource when process is running on Amazon ECS environment +// with Metadata v4 with the Fargate Launch type. +func TestDetectV4LaunchTypeFargate(t *testing.T) { + testServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { + if strings.HasSuffix(req.URL.String(), "/task") { + content, err := os.ReadFile("testdata/metadatav4-response-task-fargate.json") + if err == nil { + res.Write(content) + } + } else { + content, err := os.ReadFile("testdata/metadatav4-response-container-fargate.json") + if err == nil { + res.Write(content) + } + } + })) + defer func() { testServer.Close() }() + + os.Clearenv() + _ = os.Setenv(metadataV3EnvVar, "3") + _ = os.Setenv(metadataV4EnvVar, testServer.URL) + + detectorUtils := new(MockDetectorUtils) + + detectorUtils.On("getContainerName").Return("container-Name", nil) + detectorUtils.On("getContainerID").Return("0123456789A", nil) + + attributes := []attribute.KeyValue{ + semconv.CloudProviderAWS, + semconv.CloudPlatformAWSECS, + semconv.ContainerNameKey.String("container-Name"), + semconv.ContainerIDKey.String("0123456789A"), + semconv.AWSECSContainerARNKey.String("arn:aws:ecs:us-west-2:111122223333:container/05966557-f16c-49cb-9352-24b3a0dcd0e1"), + semconv.AWSECSClusterARNKey.String("arn:aws:ecs:us-west-2:111122223333:cluster/default"), + semconv.AWSECSLaunchtypeKey.String("fargate"), + semconv.AWSECSTaskARNKey.String("arn:aws:ecs:us-west-2:111122223333:task/default/e9028f8d5d8e4f258373e7b93ce9a3c3"), + semconv.AWSECSTaskFamilyKey.String("curltest"), + semconv.AWSECSTaskRevisionKey.String("3"), + semconv.AWSLogGroupNamesKey.String("/ecs/containerlogs"), + semconv.AWSLogGroupARNsKey.String("arn:aws:logs:us-west-2:111122223333:log-group:/ecs/containerlogs:*"), + semconv.AWSLogStreamNamesKey.String("ecs/curl/cd189a933e5849daa93386466019ab50"), + semconv.AWSLogStreamARNsKey.String("arn:aws:logs:us-west-2:111122223333:log-group:/ecs/containerlogs:log-stream:ecs/curl/cd189a933e5849daa93386466019ab50"), + } + expectedResource := resource.NewWithAttributes(semconv.SchemaURL, attributes...) + detector := &resourceDetector{utils: detectorUtils} + res, err := detector.Detect(context.Background()) + + assert.Equal(t, err, nil, "Detector should not file") + assert.Equal(t, expectedResource, res, "Resource returned is incorrect") +} + // returns empty resource when detector cannot read container ID. func TestDetectCannotReadContainerID(t *testing.T) { os.Clearenv() diff --git a/detectors/aws/ecs/go.sum b/detectors/aws/ecs/go.sum index b6e8c2d4bfe..51e9d87306a 100644 --- a/detectors/aws/ecs/go.sum +++ b/detectors/aws/ecs/go.sum @@ -1,5 +1,3 @@ -github.com/brunoscheufler/aws-ecs-metadata-go v0.0.0-20220809142905-71ffda5b3a7b h1:sDlCP3q7DzAycTPuhV2U08iCoOwSO6SV4qOBlm012Vs= -github.com/brunoscheufler/aws-ecs-metadata-go v0.0.0-20220809142905-71ffda5b3a7b/go.mod h1:5BBxNAuQFkDjY3l9UHlJ8NnnM6NEi0bocZR2xc3xGxU= github.com/brunoscheufler/aws-ecs-metadata-go v0.0.0-20220812150832-b6b31c6eeeaf h1:WCnJxXZXx9c8gwz598wvdqmu+YTzB9wx2X1OovK3Le8= github.com/brunoscheufler/aws-ecs-metadata-go v0.0.0-20220812150832-b6b31c6eeeaf/go.mod h1:CeKhh8xSs3WZAc50xABMxu+FlfAAd5PNumo7NfOv7EE= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/detectors/aws/ecs/testdata/metadatav4-response-container.json b/detectors/aws/ecs/testdata/metadatav4-response-container-ec2.json similarity index 100% rename from detectors/aws/ecs/testdata/metadatav4-response-container.json rename to detectors/aws/ecs/testdata/metadatav4-response-container-ec2.json diff --git a/detectors/aws/ecs/testdata/metadatav4-response-container-fargate.json b/detectors/aws/ecs/testdata/metadatav4-response-container-fargate.json new file mode 100644 index 00000000000..ccbe70bc44a --- /dev/null +++ b/detectors/aws/ecs/testdata/metadatav4-response-container-fargate.json @@ -0,0 +1,50 @@ +{ + "DockerId": "cd189a933e5849daa93386466019ab50-2495160603", + "Name": "curl", + "DockerName": "curl", + "Image": "111122223333.dkr.ecr.us-west-2.amazonaws.com/curltest:latest", + "ImageID": "sha256:25f3695bedfb454a50f12d127839a68ad3caf91e451c1da073db34c542c4d2cb", + "Labels": { + "com.amazonaws.ecs.cluster": "arn:aws:ecs:us-west-2:111122223333:cluster/default", + "com.amazonaws.ecs.container-name": "curl", + "com.amazonaws.ecs.task-arn": "arn:aws:ecs:us-west-2:111122223333:task/default/cd189a933e5849daa93386466019ab50", + "com.amazonaws.ecs.task-definition-family": "curltest", + "com.amazonaws.ecs.task-definition-version": "2" + }, + "DesiredStatus": "RUNNING", + "KnownStatus": "RUNNING", + "Limits": { + "CPU": 10, + "Memory": 128 + }, + "CreatedAt": "2020-10-08T20:09:11.44527186Z", + "StartedAt": "2020-10-08T20:09:11.44527186Z", + "Type": "NORMAL", + "Networks": [ + { + "NetworkMode": "awsvpc", + "IPv4Addresses": [ + "192.0.2.3" + ], + "AttachmentIndex": 0, + "MACAddress": "0a:de:f6:10:51:e5", + "IPv4SubnetCIDRBlock": "192.0.2.0/24", + "DomainNameServers": [ + "192.0.2.2" + ], + "DomainNameSearchList": [ + "us-west-2.compute.internal" + ], + "PrivateDNSName": "ip-10-0-0-222.us-west-2.compute.internal", + "SubnetGatewayIpv4Address": "192.0.2.0/24" + } + ], + "ContainerARN": "arn:aws:ecs:us-west-2:111122223333:container/05966557-f16c-49cb-9352-24b3a0dcd0e1", + "LogOptions": { + "awslogs-create-group": "true", + "awslogs-group": "/ecs/containerlogs", + "awslogs-region": "us-west-2", + "awslogs-stream": "ecs/curl/cd189a933e5849daa93386466019ab50" + }, + "LogDriver": "awslogs" +} \ No newline at end of file diff --git a/detectors/aws/ecs/testdata/metadatav4-response-task.json b/detectors/aws/ecs/testdata/metadatav4-response-task-ec2.json similarity index 100% rename from detectors/aws/ecs/testdata/metadatav4-response-task.json rename to detectors/aws/ecs/testdata/metadatav4-response-task-ec2.json diff --git a/detectors/aws/ecs/testdata/metadatav4-response-task-fargate.json b/detectors/aws/ecs/testdata/metadatav4-response-task-fargate.json new file mode 100644 index 00000000000..7979db708da --- /dev/null +++ b/detectors/aws/ecs/testdata/metadatav4-response-task-fargate.json @@ -0,0 +1,77 @@ +{ + "Cluster": "arn:aws:ecs:us-west-2:111122223333:cluster/default", + "TaskARN": "arn:aws:ecs:us-west-2:111122223333:task/default/e9028f8d5d8e4f258373e7b93ce9a3c3", + "Family": "curltest", + "Revision": "3", + "DesiredStatus": "RUNNING", + "KnownStatus": "RUNNING", + "Limits": { + "CPU": 0.25, + "Memory": 512 + }, + "PullStartedAt": "2020-10-08T20:47:16.053330955Z", + "PullStoppedAt": "2020-10-08T20:47:19.592684631Z", + "AvailabilityZone": "us-west-2a", + "Containers": [ + { + "DockerId": "e9028f8d5d8e4f258373e7b93ce9a3c3-2495160603", + "Name": "curl", + "DockerName": "curl", + "Image": "111122223333.dkr.ecr.us-west-2.amazonaws.com/curltest:latest", + "ImageID": "sha256:25f3695bedfb454a50f12d127839a68ad3caf91e451c1da073db34c542c4d2cb", + "Labels": { + "com.amazonaws.ecs.cluster": "arn:aws:ecs:us-west-2:111122223333:cluster/default", + "com.amazonaws.ecs.container-name": "curl", + "com.amazonaws.ecs.task-arn": "arn:aws:ecs:us-west-2:111122223333:task/default/e9028f8d5d8e4f258373e7b93ce9a3c3", + "com.amazonaws.ecs.task-definition-family": "curltest", + "com.amazonaws.ecs.task-definition-version": "3" + }, + "DesiredStatus": "RUNNING", + "KnownStatus": "RUNNING", + "Limits": { + "CPU": 10, + "Memory": 128 + }, + "CreatedAt": "2020-10-08T20:47:20.567813946Z", + "StartedAt": "2020-10-08T20:47:20.567813946Z", + "Type": "NORMAL", + "Networks": [ + { + "NetworkMode": "awsvpc", + "IPv4Addresses": [ + "192.0.2.3" + ], + "IPv6Addresses": [ + "2001:dB8:10b:1a00:32bf:a372:d80f:e958" + ], + "AttachmentIndex": 0, + "MACAddress": "02:b7:20:19:72:39", + "IPv4SubnetCIDRBlock": "192.0.2.0/24", + "IPv6SubnetCIDRBlock": "2600:1f13:10b:1a00::/64", + "DomainNameServers": [ + "192.0.2.2" + ], + "DomainNameSearchList": [ + "us-west-2.compute.internal" + ], + "PrivateDNSName": "ip-172-31-30-173.us-west-2.compute.internal", + "SubnetGatewayIpv4Address": "192.0.2.0/24" + } + ], + "ClockDrift": { + "ClockErrorBound": 0.5458234999999999, + "ReferenceTimestamp": "2021-09-07T16:57:44Z", + "ClockSynchronizationStatus": "SYNCHRONIZED" + }, + "ContainerARN": "arn:aws:ecs:us-west-2:111122223333:container/1bdcca8b-f905-4ee6-885c-4064cb70f6e6", + "LogOptions": { + "awslogs-create-group": "true", + "awslogs-group": "/ecs/containerlogs", + "awslogs-region": "us-west-2", + "awslogs-stream": "ecs/curl/e9028f8d5d8e4f258373e7b93ce9a3c3" + }, + "LogDriver": "awslogs" + } + ], + "LaunchType": "FARGATE" +} \ No newline at end of file From 400645ff7288c41f2dc5befd9d3ad7a1615c8301 Mon Sep 17 00:00:00 2001 From: Michele Mancioppi Date: Sun, 23 Oct 2022 14:49:18 +0200 Subject: [PATCH 7/8] Rebase --- detectors/aws/ecs/ecs_test.go | 4 ++-- detectors/aws/ecs/go.mod | 2 +- detectors/aws/ecs/go.sum | 20 +++++++++----------- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/detectors/aws/ecs/ecs_test.go b/detectors/aws/ecs/ecs_test.go index e43c2e42091..bfd13e697ec 100644 --- a/detectors/aws/ecs/ecs_test.go +++ b/detectors/aws/ecs/ecs_test.go @@ -87,7 +87,7 @@ func TestDetectV4LaunchTypeEc2(t *testing.T) { } } })) - defer func() { testServer.Close() }() + defer testServer.Close() os.Clearenv() _ = os.Setenv(metadataV3EnvVar, "3") @@ -138,7 +138,7 @@ func TestDetectV4LaunchTypeFargate(t *testing.T) { } } })) - defer func() { testServer.Close() }() + defer testServer.Close() os.Clearenv() _ = os.Setenv(metadataV3EnvVar, "3") diff --git a/detectors/aws/ecs/go.mod b/detectors/aws/ecs/go.mod index 53cc4a2c3eb..b13768ed45a 100644 --- a/detectors/aws/ecs/go.mod +++ b/detectors/aws/ecs/go.mod @@ -3,13 +3,13 @@ module go.opentelemetry.io/contrib/detectors/aws/ecs go 1.18 require ( + github.com/brunoscheufler/aws-ecs-metadata-go v0.0.0-20220812150832-b6b31c6eeeaf github.com/stretchr/testify v1.8.0 go.opentelemetry.io/otel v1.11.1 go.opentelemetry.io/otel/sdk v1.11.1 ) require ( - github.com/brunoscheufler/aws-ecs-metadata-go v0.0.0-20220812150832-b6b31c6eeeaf // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-logr/logr v1.2.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect diff --git a/detectors/aws/ecs/go.sum b/detectors/aws/ecs/go.sum index 51e9d87306a..5f6cd3c7c8b 100644 --- a/detectors/aws/ecs/go.sum +++ b/detectors/aws/ecs/go.sum @@ -8,8 +8,7 @@ github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -18,15 +17,14 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -go.opentelemetry.io/otel v1.9.0 h1:8WZNQFIB2a71LnANS9JeyidJKKGOOremcUtb/OtHISw= -go.opentelemetry.io/otel v1.9.0/go.mod h1:np4EoPGzoPs3O67xUVNoPPcmSvsfOxNlNA4F4AC+0Eo= -go.opentelemetry.io/otel/sdk v1.9.0 h1:LNXp1vrr83fNXTHgU8eO89mhzxb/bbWAsHG6fNf3qWo= -go.opentelemetry.io/otel/sdk v1.9.0/go.mod h1:AEZc8nt5bd2F7BC24J5R0mrjYnpEgYHyTcM/vrSple4= -go.opentelemetry.io/otel/trace v1.9.0 h1:oZaCNJUjWcg60VXWee8lJKlqhPbXAPB51URuR47pQYc= -go.opentelemetry.io/otel/trace v1.9.0/go.mod h1:2737Q0MuG8q1uILYm2YYVkAyLtOofiTNGg6VODnOiPo= -golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20220808155132-1c4a2a72c664 h1:v1W7bwXHsnLLloWYTVEdvGvA7BHMeBYsPcF0GLDxIRs= -golang.org/x/sys v0.0.0-20220808155132-1c4a2a72c664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +go.opentelemetry.io/otel v1.11.1 h1:4WLLAmcfkmDk2ukNXJyq3/kiz/3UzCaYq6PskJsaou4= +go.opentelemetry.io/otel v1.11.1/go.mod h1:1nNhXBbWSD0nsL38H6btgnFN2k4i0sNLHNNMZMSbUGE= +go.opentelemetry.io/otel/sdk v1.11.1 h1:F7KmQgoHljhUuJyA+9BiU+EkJfyX5nVVF4wyzWZpKxs= +go.opentelemetry.io/otel/sdk v1.11.1/go.mod h1:/l3FE4SupHJ12TduVjUkZtlfFqDCQJlOlithYrdktys= +go.opentelemetry.io/otel/trace v1.11.1 h1:ofxdnzsNrGBYXbP7t7zpUK281+go5rF7dvdIZXF8gdQ= +go.opentelemetry.io/otel/trace v1.11.1/go.mod h1:f/Q9G7vzk5u91PhbmKbg1Qn0rzH1LJ4vbPHFGkTPtOk= +golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 h1:h+EGohizhe9XlX18rfpa8k8RAc5XyaeamM+0VHRd4lc= +golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From c84218ee0ea2ae8994e46d2d9e478a9b154904dd Mon Sep 17 00:00:00 2001 From: Michele Mancioppi Date: Mon, 24 Oct 2022 17:28:24 +0200 Subject: [PATCH 8/8] Fix integration tests, fix behavior on Windows After a surreal session of debugging, it turns out that httptest.NewServer fails on Windows by failing to bind the server socket unless the GoLang package name contains "test". To deal with that, I split the tests between "integration tests" needing a HTTP server into "ecs/test", and the other in "ecs". In order to work around the need of being resilient to the lack of /proc/self/cgroup (which happens in our tests but, above all, when running containers on Windows), the ECVS detector is now more lenient to not finding the container id. --- detectors/aws/ecs/ecs.go | 22 +-- detectors/aws/ecs/ecs_test.go | 126 ++------------- detectors/aws/ecs/test/ecs_test.go | 149 ++++++++++++++++++ .../metadatav4-response-container-ec2.json | 0 ...metadatav4-response-container-fargate.json | 0 .../metadatav4-response-task-ec2.json | 0 .../metadatav4-response-task-fargate.json | 0 .../aws-sdk-go-v2/otelaws/test/aws_test.go | 6 +- 8 files changed, 173 insertions(+), 130 deletions(-) create mode 100644 detectors/aws/ecs/test/ecs_test.go rename detectors/aws/ecs/{testdata => test}/metadatav4-response-container-ec2.json (100%) rename detectors/aws/ecs/{testdata => test}/metadatav4-response-container-fargate.json (100%) rename detectors/aws/ecs/{testdata => test}/metadatav4-response-task-ec2.json (100%) rename detectors/aws/ecs/{testdata => test}/metadatav4-response-task-fargate.json (100%) diff --git a/detectors/aws/ecs/ecs.go b/detectors/aws/ecs/ecs.go index e73dcf83e80..34c3ba83dc7 100644 --- a/detectors/aws/ecs/ecs.go +++ b/detectors/aws/ecs/ecs.go @@ -21,6 +21,7 @@ import ( "net/http" "os" "regexp" + "runtime" "strings" ecsmetadata "github.com/brunoscheufler/aws-ecs-metadata-go" @@ -41,15 +42,9 @@ const ( var ( empty = resource.Empty() - errCannotReadContainerID = errors.New("failed to read container ID from cGroupFile") errCannotReadContainerName = errors.New("failed to read hostname") - errCannotReadCGroupFile = errors.New("ECS resource detector failed to read cGroupFile") - errCannotRetrieveMetadataV4 = errors.New("ECS resource detector failed to retrieve metadata from the ECS Metadata v4 container endpoint") - errCannotRetrieveMetadataV4Task = errors.New("ECS resource detector failed to retrieve metadata from the ECS Metadata v4 task endpoint") - errCannotRetrieveLogsGroupMetadataV4 = errors.New("The ECS Metadata v4 did not return a AwsLogGroup name") - errCannotRetrieveLogsStreamMetadataV4 = errors.New("The ECS Metadata v4 did not return a AwsLogStream name") - errCannotParseAwsRegionMetadataV4 = errors.New("Cannot parse the AWS region our of the Container ARN returned by the ECS Metadata v4 container endpoint") - errCannotParseAwsAccountMetadataV4 = errors.New("Cannot parse the AWS account our of the Container ARN returned by the ECS Metadata v4 container endpoint") + errCannotRetrieveLogsGroupMetadataV4 = errors.New("the ECS Metadata v4 did not return a AwsLogGroup name") + errCannotRetrieveLogsStreamMetadataV4 = errors.New("the ECS Metadata v4 did not return a AwsLogStream name") ) // Create interface for methods needing to be mocked. @@ -180,9 +175,16 @@ func (detector *resourceDetector) getLogsAttributes(metadata *ecsmetadata.Contai // returns docker container ID from default c group path. func (ecsUtils ecsDetectorUtils) getContainerID() (string, error) { + if runtime.GOOS != "linux" { + // Cgroups are used only under Linux. + return "", nil + } + fileData, err := os.ReadFile(defaultCgroupPath) if err != nil { - return "", errCannotReadCGroupFile + // Cgroups file not found. + // For example, windows; or when running integration tests outside of a container. + return "", nil } splitData := strings.Split(strings.TrimSpace(string(fileData)), "\n") for _, str := range splitData { @@ -190,7 +192,7 @@ func (ecsUtils ecsDetectorUtils) getContainerID() (string, error) { return str[len(str)-containerIDLength:], nil } } - return "", errCannotReadContainerID + return "", nil } // returns host name reported by the kernel. diff --git a/detectors/aws/ecs/ecs_test.go b/detectors/aws/ecs/ecs_test.go index bfd13e697ec..4d155809da8 100644 --- a/detectors/aws/ecs/ecs_test.go +++ b/detectors/aws/ecs/ecs_test.go @@ -16,20 +16,15 @@ package ecs import ( "context" - "encoding/json" - http "net/http" - "net/http/httptest" "os" - "strings" "testing" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - - ecsmetadata "github.com/brunoscheufler/aws-ecs-metadata-go" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/sdk/resource" semconv "go.opentelemetry.io/otel/semconv/v1.12.0" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" ) // Create interface for functions that need to be mocked. @@ -47,7 +42,7 @@ func (detectorUtils *MockDetectorUtils) getContainerName() (string, error) { return args.String(0), args.Error(1) } -// succesfully returns resource when process is running on Amazon ECS environment +// successfully returns resource when process is running on Amazon ECS environment // with no Metadata v4. func TestDetectV3(t *testing.T) { os.Clearenv() @@ -71,125 +66,29 @@ func TestDetectV3(t *testing.T) { assert.Equal(t, expectedResource, res, "Resource returned is incorrect") } -// succesfully returns resource when process is running on Amazon ECS environment -// with Metadata v4 with the EC2 Launch type. -func TestDetectV4LaunchTypeEc2(t *testing.T) { - testServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { - if strings.HasSuffix(req.URL.String(), "/task") { - content, err := os.ReadFile("testdata/metadatav4-response-task-ec2.json") - if err == nil { - res.Write(content) - } - } else { - content, err := os.ReadFile("testdata/metadatav4-response-container-ec2.json") - if err == nil { - res.Write(content) - } - } - })) - defer testServer.Close() - - os.Clearenv() - _ = os.Setenv(metadataV3EnvVar, "3") - _ = os.Setenv(metadataV4EnvVar, testServer.URL) - - detectorUtils := new(MockDetectorUtils) - - detectorUtils.On("getContainerName").Return("container-Name", nil) - detectorUtils.On("getContainerID").Return("0123456789A", nil) - - attributes := []attribute.KeyValue{ - semconv.CloudProviderAWS, - semconv.CloudPlatformAWSECS, - semconv.ContainerNameKey.String("container-Name"), - semconv.ContainerIDKey.String("0123456789A"), - semconv.AWSECSContainerARNKey.String("arn:aws:ecs:us-west-2:111122223333:container/0206b271-b33f-47ab-86c6-a0ba208a70a9"), - semconv.AWSECSClusterARNKey.String("arn:aws:ecs:us-west-2:111122223333:cluster/default"), - semconv.AWSECSLaunchtypeKey.String("ec2"), - semconv.AWSECSTaskARNKey.String("arn:aws:ecs:us-west-2:111122223333:task/default/158d1c8083dd49d6b527399fd6414f5c"), - semconv.AWSECSTaskFamilyKey.String("curltest"), - semconv.AWSECSTaskRevisionKey.String("26"), - semconv.AWSLogGroupNamesKey.String("/ecs/metadata"), - semconv.AWSLogGroupARNsKey.String("arn:aws:logs:us-west-2:111122223333:log-group:/ecs/metadata:*"), - semconv.AWSLogStreamNamesKey.String("ecs/curl/8f03e41243824aea923aca126495f665"), - semconv.AWSLogStreamARNsKey.String("arn:aws:logs:us-west-2:111122223333:log-group:/ecs/metadata:log-stream:ecs/curl/8f03e41243824aea923aca126495f665"), - } - expectedResource := resource.NewWithAttributes(semconv.SchemaURL, attributes...) - detector := &resourceDetector{utils: detectorUtils} - res, err := detector.Detect(context.Background()) - - assert.Equal(t, err, nil, "Detector should not file") - assert.Equal(t, expectedResource, res, "Resource returned is incorrect") -} - -// succesfully returns resource when process is running on Amazon ECS environment -// with Metadata v4 with the Fargate Launch type. -func TestDetectV4LaunchTypeFargate(t *testing.T) { - testServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { - if strings.HasSuffix(req.URL.String(), "/task") { - content, err := os.ReadFile("testdata/metadatav4-response-task-fargate.json") - if err == nil { - res.Write(content) - } - } else { - content, err := os.ReadFile("testdata/metadatav4-response-container-fargate.json") - if err == nil { - res.Write(content) - } - } - })) - defer testServer.Close() - +// returns empty resource when detector cannot read container ID. +func TestDetectCannotReadContainerID(t *testing.T) { os.Clearenv() _ = os.Setenv(metadataV3EnvVar, "3") - _ = os.Setenv(metadataV4EnvVar, testServer.URL) - detectorUtils := new(MockDetectorUtils) detectorUtils.On("getContainerName").Return("container-Name", nil) - detectorUtils.On("getContainerID").Return("0123456789A", nil) + detectorUtils.On("getContainerID").Return("", nil) attributes := []attribute.KeyValue{ semconv.CloudProviderAWS, semconv.CloudPlatformAWSECS, semconv.ContainerNameKey.String("container-Name"), - semconv.ContainerIDKey.String("0123456789A"), - semconv.AWSECSContainerARNKey.String("arn:aws:ecs:us-west-2:111122223333:container/05966557-f16c-49cb-9352-24b3a0dcd0e1"), - semconv.AWSECSClusterARNKey.String("arn:aws:ecs:us-west-2:111122223333:cluster/default"), - semconv.AWSECSLaunchtypeKey.String("fargate"), - semconv.AWSECSTaskARNKey.String("arn:aws:ecs:us-west-2:111122223333:task/default/e9028f8d5d8e4f258373e7b93ce9a3c3"), - semconv.AWSECSTaskFamilyKey.String("curltest"), - semconv.AWSECSTaskRevisionKey.String("3"), - semconv.AWSLogGroupNamesKey.String("/ecs/containerlogs"), - semconv.AWSLogGroupARNsKey.String("arn:aws:logs:us-west-2:111122223333:log-group:/ecs/containerlogs:*"), - semconv.AWSLogStreamNamesKey.String("ecs/curl/cd189a933e5849daa93386466019ab50"), - semconv.AWSLogStreamARNsKey.String("arn:aws:logs:us-west-2:111122223333:log-group:/ecs/containerlogs:log-stream:ecs/curl/cd189a933e5849daa93386466019ab50"), + semconv.ContainerIDKey.String(""), } expectedResource := resource.NewWithAttributes(semconv.SchemaURL, attributes...) detector := &resourceDetector{utils: detectorUtils} res, err := detector.Detect(context.Background()) - assert.Equal(t, err, nil, "Detector should not file") + assert.Equal(t, nil, err) assert.Equal(t, expectedResource, res, "Resource returned is incorrect") } -// returns empty resource when detector cannot read container ID. -func TestDetectCannotReadContainerID(t *testing.T) { - os.Clearenv() - _ = os.Setenv(metadataV3EnvVar, "3") - _ = os.Setenv(metadataV4EnvVar, "4") - detectorUtils := new(MockDetectorUtils) - - detectorUtils.On("getContainerName").Return("container-Name", nil) - detectorUtils.On("getContainerID").Return("", errCannotReadContainerID) - - detector := &resourceDetector{utils: detectorUtils} - res, err := detector.Detect(context.Background()) - - assert.Equal(t, errCannotReadContainerID, err) - assert.Equal(t, 0, len(res.Attributes())) -} - // returns empty resource when detector cannot read container Name. func TestDetectCannotReadContainerName(t *testing.T) { os.Clearenv() @@ -207,13 +106,6 @@ func TestDetectCannotReadContainerName(t *testing.T) { assert.Equal(t, 0, len(res.Attributes())) } -func TestDetectV4Parse(t *testing.T) { - content, _ := os.ReadFile("testdata/metadatav4-response-task.json") - - taskMetadata := &ecsmetadata.TaskMetadataV4{} - json.Unmarshal(content, taskMetadata) -} - // returns empty resource when process is not running ECS. func TestReturnsIfNoEnvVars(t *testing.T) { os.Clearenv() diff --git a/detectors/aws/ecs/test/ecs_test.go b/detectors/aws/ecs/test/ecs_test.go new file mode 100644 index 00000000000..1cdcc968623 --- /dev/null +++ b/detectors/aws/ecs/test/ecs_test.go @@ -0,0 +1,149 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ecs + +import ( + "context" + "net/http" + "net/http/httptest" + "os" + "strings" + "testing" + + ecs "go.opentelemetry.io/contrib/detectors/aws/ecs" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/sdk/resource" + semconv "go.opentelemetry.io/otel/semconv/v1.12.0" + + "github.com/stretchr/testify/assert" +) + +const ( + metadataV4EnvVar = "ECS_CONTAINER_METADATA_URI_V4" +) + +// successfully returns resource when process is running on Amazon ECS environment +// with Metadata v4 with the EC2 Launch type. +func TestDetectV4LaunchTypeEc2(t *testing.T) { + testServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { + if strings.HasSuffix(req.URL.String(), "/task") { + content, err := os.ReadFile("metadatav4-response-task-ec2.json") + if err == nil { + _, err = res.Write(content) + if err != nil { + t.Fatal(err) + } + } + } else { + content, err := os.ReadFile("metadatav4-response-container-ec2.json") + if err == nil { + _, err = res.Write(content) + if err != nil { + t.Fatal(err) + } + } + } + })) + defer testServer.Close() + + os.Clearenv() + _ = os.Setenv(metadataV4EnvVar, testServer.URL) + + hostname, err := os.Hostname() + assert.NoError(t, err, "Error") + + attributes := []attribute.KeyValue{ + semconv.CloudProviderAWS, + semconv.CloudPlatformAWSECS, + semconv.ContainerNameKey.String(hostname), + // We are not running the test in an actual container, + // the container id is tested with mocks of the cgroup + // file in the unit tests + semconv.ContainerIDKey.String(""), + semconv.AWSECSContainerARNKey.String("arn:aws:ecs:us-west-2:111122223333:container/0206b271-b33f-47ab-86c6-a0ba208a70a9"), + semconv.AWSECSClusterARNKey.String("arn:aws:ecs:us-west-2:111122223333:cluster/default"), + semconv.AWSECSLaunchtypeKey.String("ec2"), + semconv.AWSECSTaskARNKey.String("arn:aws:ecs:us-west-2:111122223333:task/default/158d1c8083dd49d6b527399fd6414f5c"), + semconv.AWSECSTaskFamilyKey.String("curltest"), + semconv.AWSECSTaskRevisionKey.String("26"), + semconv.AWSLogGroupNamesKey.String("/ecs/metadata"), + semconv.AWSLogGroupARNsKey.String("arn:aws:logs:us-west-2:111122223333:log-group:/ecs/metadata:*"), + semconv.AWSLogStreamNamesKey.String("ecs/curl/8f03e41243824aea923aca126495f665"), + semconv.AWSLogStreamARNsKey.String("arn:aws:logs:us-west-2:111122223333:log-group:/ecs/metadata:log-stream:ecs/curl/8f03e41243824aea923aca126495f665"), + } + expectedResource := resource.NewWithAttributes(semconv.SchemaURL, attributes...) + detector := ecs.NewResourceDetector() + res, err := detector.Detect(context.Background()) + + assert.Equal(t, nil, err, "Detector should not fail") + assert.Equal(t, expectedResource, res, "Resource returned is incorrect") +} + +// successfully returns resource when process is running on Amazon ECS environment +// with Metadata v4 with the Fargate Launch type. +func TestDetectV4LaunchTypeFargate(t *testing.T) { + testServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { + if strings.HasSuffix(req.URL.String(), "/task") { + content, err := os.ReadFile("metadatav4-response-task-fargate.json") + if err == nil { + _, err = res.Write(content) + if err != nil { + panic(err) + } + } + } else { + content, err := os.ReadFile("metadatav4-response-container-fargate.json") + if err == nil { + _, err = res.Write(content) + if err != nil { + panic(err) + } + } + } + })) + defer testServer.Close() + + os.Clearenv() + _ = os.Setenv(metadataV4EnvVar, testServer.URL) + + hostname, err := os.Hostname() + assert.NoError(t, err, "Error") + + attributes := []attribute.KeyValue{ + semconv.CloudProviderAWS, + semconv.CloudPlatformAWSECS, + semconv.ContainerNameKey.String(hostname), + // We are not running the test in an actual container, + // the container id is tested with mocks of the cgroup + // file in the unit tests + semconv.ContainerIDKey.String(""), + semconv.AWSECSContainerARNKey.String("arn:aws:ecs:us-west-2:111122223333:container/05966557-f16c-49cb-9352-24b3a0dcd0e1"), + semconv.AWSECSClusterARNKey.String("arn:aws:ecs:us-west-2:111122223333:cluster/default"), + semconv.AWSECSLaunchtypeKey.String("fargate"), + semconv.AWSECSTaskARNKey.String("arn:aws:ecs:us-west-2:111122223333:task/default/e9028f8d5d8e4f258373e7b93ce9a3c3"), + semconv.AWSECSTaskFamilyKey.String("curltest"), + semconv.AWSECSTaskRevisionKey.String("3"), + semconv.AWSLogGroupNamesKey.String("/ecs/containerlogs"), + semconv.AWSLogGroupARNsKey.String("arn:aws:logs:us-west-2:111122223333:log-group:/ecs/containerlogs:*"), + semconv.AWSLogStreamNamesKey.String("ecs/curl/cd189a933e5849daa93386466019ab50"), + semconv.AWSLogStreamARNsKey.String("arn:aws:logs:us-west-2:111122223333:log-group:/ecs/containerlogs:log-stream:ecs/curl/cd189a933e5849daa93386466019ab50"), + } + expectedResource := resource.NewWithAttributes(semconv.SchemaURL, attributes...) + detector := ecs.NewResourceDetector() + res, err := detector.Detect(context.Background()) + + assert.Equal(t, nil, err, "Detector should not fail") + assert.Equal(t, expectedResource, res, "Resource returned is incorrect") +} diff --git a/detectors/aws/ecs/testdata/metadatav4-response-container-ec2.json b/detectors/aws/ecs/test/metadatav4-response-container-ec2.json similarity index 100% rename from detectors/aws/ecs/testdata/metadatav4-response-container-ec2.json rename to detectors/aws/ecs/test/metadatav4-response-container-ec2.json diff --git a/detectors/aws/ecs/testdata/metadatav4-response-container-fargate.json b/detectors/aws/ecs/test/metadatav4-response-container-fargate.json similarity index 100% rename from detectors/aws/ecs/testdata/metadatav4-response-container-fargate.json rename to detectors/aws/ecs/test/metadatav4-response-container-fargate.json diff --git a/detectors/aws/ecs/testdata/metadatav4-response-task-ec2.json b/detectors/aws/ecs/test/metadatav4-response-task-ec2.json similarity index 100% rename from detectors/aws/ecs/testdata/metadatav4-response-task-ec2.json rename to detectors/aws/ecs/test/metadatav4-response-task-ec2.json diff --git a/detectors/aws/ecs/testdata/metadatav4-response-task-fargate.json b/detectors/aws/ecs/test/metadatav4-response-task-fargate.json similarity index 100% rename from detectors/aws/ecs/testdata/metadatav4-response-task-fargate.json rename to detectors/aws/ecs/test/metadatav4-response-task-fargate.json diff --git a/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/test/aws_test.go b/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/test/aws_test.go index 2280821f13c..ea87660d0af 100644 --- a/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/test/aws_test.go +++ b/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws/test/aws_test.go @@ -91,7 +91,7 @@ func TestAppendMiddlewares(t *testing.T) { } for name, c := range cases { - server := httptest.NewServer(http.HandlerFunc( + srv := httptest.NewServer(http.HandlerFunc( func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(c.responseStatus) _, err := w.Write(c.responseBody) @@ -109,7 +109,7 @@ func TestAppendMiddlewares(t *testing.T) { EndpointResolverWithOptions: aws.EndpointResolverWithOptionsFunc( func(service, region string, _ ...interface{}) (aws.Endpoint, error) { return aws.Endpoint{ - URL: server.URL, + URL: srv.URL, SigningName: "route53", }, nil }, @@ -151,6 +151,6 @@ func TestAppendMiddlewares(t *testing.T) { assert.Contains(t, attrs, attribute.String("aws.operation", "ChangeResourceRecordSets")) }) - server.Close() + srv.Close() } }