Skip to content

Commit 13d604f

Browse files
authored
Fixed Agent crashing in AWS Fargate (#1493)
1 parent 5476836 commit 13d604f

File tree

2 files changed

+126
-7
lines changed

2 files changed

+126
-7
lines changed

pkg/host/info.go

Lines changed: 71 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -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) {
121140
func (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+
}

pkg/host/info_test.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ package host
77

88
import (
99
"context"
10+
"fmt"
11+
"net/http"
12+
"net/http/httptest"
1013
"os"
1114
"strings"
1215
"testing"
@@ -633,3 +636,55 @@ func TestInfo_ParseOsReleaseFile(t *testing.T) {
633636
})
634637
}
635638
}
639+
640+
func TestInfo_containerIDFromECS(t *testing.T) {
641+
ctx := context.Background()
642+
643+
tests := []struct {
644+
name string
645+
handler http.HandlerFunc
646+
wantID string
647+
expectErr bool
648+
}{
649+
{
650+
name: "Test 1: success - valid JSON",
651+
handler: func(w http.ResponseWriter, r *http.Request) {
652+
w.WriteHeader(http.StatusOK)
653+
fmt.Fprintln(w, `{"DockerId":"c1234567890"}`)
654+
},
655+
wantID: "c1234567890",
656+
expectErr: false,
657+
},
658+
{
659+
name: "Test 2: non-200 response",
660+
handler: func(w http.ResponseWriter, r *http.Request) {
661+
w.WriteHeader(http.StatusInternalServerError)
662+
},
663+
expectErr: true,
664+
},
665+
{
666+
name: "Test 3: invalid JSON",
667+
handler: func(w http.ResponseWriter, r *http.Request) {
668+
w.WriteHeader(http.StatusOK)
669+
fmt.Fprintln(w, `not-a-json`)
670+
},
671+
expectErr: true,
672+
},
673+
}
674+
675+
for _, tt := range tests {
676+
t.Run(tt.name, func(t *testing.T) {
677+
srv := httptest.NewServer(tt.handler)
678+
defer srv.Close()
679+
680+
info := NewInfo()
681+
id, err := info.containerIDFromECS(ctx, srv.URL)
682+
if tt.expectErr {
683+
require.Error(t, err)
684+
} else {
685+
require.NoError(t, err)
686+
assert.Equal(t, tt.wantID, id)
687+
}
688+
})
689+
}
690+
}

0 commit comments

Comments
 (0)