@@ -9,9 +9,11 @@ import (
99 "bufio"
1010 "bytes"
1111 "context"
12+ "encoding/json"
1213 "errors"
1314 "fmt"
1415 "io"
16+ "net/http"
1517 "os"
1618 "regexp"
1719 "strings"
@@ -31,9 +33,13 @@ const (
3133 mountInfoLocation = "/proc/self/mountinfo"
3234 osReleaseLocation = "/etc/os-release"
3335
36+ ecsMetadataEnvV4 = "ECS_CONTAINER_METADATA_URI_V4"
37+
3438 k8sKind = "kubepods"
3539 docker = "docker"
3640 containerd = "containerd"
41+ ecsPrefix = "ecs" // AWS ECS Fargate
42+ fargate = "fargate" // AWS EKS Fargate
3743
3844 numberOfKeysAndValues = 2
3945 lengthOfContainerID = 64
@@ -112,7 +118,20 @@ func (i *Info) IsContainer() (bool, error) {
112118 }
113119 }
114120
115- return containsContainerReference (i .selfCgroupLocation )
121+ ref , err := containsContainerReference (i .selfCgroupLocation )
122+ if ref {
123+ return true , nil
124+ }
125+
126+ if os .Getenv (ecsMetadataEnvV4 ) != "" {
127+ return true , nil
128+ }
129+
130+ if err != nil {
131+ return false , err
132+ }
133+
134+ return false , nil
116135}
117136
118137// ResourceID returns a unique identifier for the resource.
@@ -121,7 +140,7 @@ func (i *Info) IsContainer() (bool, error) {
121140func (i * Info ) ResourceID (ctx context.Context ) (string , error ) {
122141 isContainer , _ := i .IsContainer ()
123142 if isContainer {
124- return i .containerID ()
143+ return i .containerID (ctx )
125144 }
126145
127146 return i .hostID (ctx )
@@ -134,7 +153,7 @@ func (i *Info) ContainerInfo(ctx context.Context) (*v1.Resource_ContainerInfo, e
134153 if err != nil {
135154 return nil , err
136155 }
137- containerId , err := i .containerID ()
156+ containerId , err := i .containerID (ctx )
138157 if err != nil {
139158 return nil , err
140159 }
@@ -203,9 +222,26 @@ func (i *Info) releaseInfo(ctx context.Context, osReleaseLocation string) (*v1.R
203222}
204223
205224// containerID returns the container ID of the current running environment.
206- func (i * Info ) containerID () (string , error ) {
207- containerID , err := containerIDFromMountInfo (i .mountInfoLocation )
208- return uuid .NewMD5 (uuid .NameSpaceDNS , []byte (containerID )).String (), err
225+ func (i * Info ) containerID (ctx context.Context ) (string , error ) {
226+ var errs error
227+
228+ // Try to get container ID from mount info first
229+ if containerIDMount , err := containerIDFromMountInfo (i .mountInfoLocation ); err == nil && containerIDMount != "" {
230+ return uuid .NewMD5 (uuid .NameSpaceDNS , []byte (containerIDMount )).String (), nil
231+ } else if err != nil {
232+ errs = errors .Join (errs , err )
233+ }
234+
235+ // Try to get container ID from ECS metadata if available
236+ if metadataURI := os .Getenv (ecsMetadataEnvV4 ); metadataURI != "" {
237+ if cid , err := i .containerIDFromECS (ctx , metadataURI ); err == nil && cid != "" {
238+ return uuid .NewMD5 (uuid .NameSpaceDNS , []byte (cid )).String (), nil
239+ } else if err != nil {
240+ errs = errors .Join (errs , err )
241+ }
242+ }
243+
244+ return "" , errs
209245}
210246
211247// containsContainerReference checks if the cgroup file contains references to container runtimes.
@@ -218,7 +254,8 @@ func containsContainerReference(cgroupFile string) (bool, error) {
218254 scanner := bufio .NewScanner (bytes .NewReader (data ))
219255 for scanner .Scan () {
220256 line := strings .TrimSpace (scanner .Text ())
221- if strings .Contains (line , k8sKind ) || strings .Contains (line , docker ) || strings .Contains (line , containerd ) {
257+ if strings .Contains (line , k8sKind ) || strings .Contains (line , docker ) || strings .Contains (line , containerd ) ||
258+ strings .Contains (line , ecsPrefix ) || strings .Contains (line , fargate ) {
222259 return true , nil
223260 }
224261 }
@@ -367,3 +404,30 @@ func mergeHostAndOsReleaseInfo(
367404 Id : osReleaseInfo [id ],
368405 }
369406}
407+
408+ func (i * Info ) containerIDFromECS (ctx context.Context , uri string ) (string , error ) {
409+ req , err := http .NewRequestWithContext (ctx , http .MethodGet , uri , nil )
410+ if err != nil {
411+ return "" , err
412+ }
413+
414+ resp , err := http .DefaultClient .Do (req )
415+ if err != nil {
416+ return "" , err
417+ }
418+ defer resp .Body .Close ()
419+
420+ if resp .StatusCode != http .StatusOK {
421+ return "" , fmt .Errorf ("metadata endpoint %s returned status %d" , uri , resp .StatusCode )
422+ }
423+
424+ var metadata struct {
425+ DockerId string `json:"DockerId"`
426+ }
427+
428+ if err = json .NewDecoder (resp .Body ).Decode (& metadata ); err != nil {
429+ return "" , err
430+ }
431+
432+ return metadata .DockerId , nil
433+ }
0 commit comments