Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/create-release-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
echo "AGENT_VERSION=$AGENT_VERSION" >> $GITHUB_ENV
echo "VERSION=$VERSION" >> $GITHUB_ENV

- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
submodules: true
# Fetch all history for all tags and branches
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/gitsecrets.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
name: Git Secrets Scan
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
path: src/github.com/aws/amazon-ecs-agent
- name: Git Secrets Scan Script
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/gomod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
submodules: true
path: src/github.com/aws/amazon-ecs-agent
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
name: Linux unit tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
submodules: true
path: src/github.com/aws/amazon-ecs-agent
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/security.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ jobs:
name: Semgrep Security Scanner
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
fetch-depth: 0

Expand Down
12 changes: 6 additions & 6 deletions .github/workflows/static.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
name: Static Analysis
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
path: src/github.com/aws/amazon-ecs-agent
- name: get GO_VERSION
Expand All @@ -27,7 +27,7 @@ jobs:
- uses: actions/setup-go@v6
with:
go-version: ${{ steps.get-go-version.outputs.GO_VERSION }}
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
path: src/github.com/aws/amazon-ecs-agent
- name: run static checks
Expand All @@ -43,7 +43,7 @@ jobs:
name: Static Analysis Init
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
path: src/github.com/aws/amazon-ecs-agent
- name: get GO_VERSION
Expand All @@ -62,7 +62,7 @@ jobs:
- uses: actions/setup-go@v6
with:
go-version: ${{ steps.get-go-version.outputs.GO_VERSION }}
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
path: src/github.com/aws/amazon-ecs-agent
- name: run static checks
Expand All @@ -78,7 +78,7 @@ jobs:
name: Cross platform build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
path: src/github.com/aws/amazon-ecs-agent
- name: get GO_VERSION
Expand All @@ -97,7 +97,7 @@ jobs:
- uses: actions/setup-go@v6
with:
go-version: ${{ steps.get-go-version.outputs.GO_VERSION }}
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
submodules: true
path: src/github.com/aws/amazon-ecs-agent
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
name: Windows unit tests
runs-on: windows-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
path: src/github.com/aws/amazon-ecs-agent
- name: get GO_VERSION
Expand All @@ -27,7 +27,7 @@ jobs:
- uses: actions/setup-go@v6
with:
go-version: ${{ steps.get-go-version.outputs.GO_VERSION_WINDOWS }}
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
submodules: true
path: src/github.com/aws/amazon-ecs-agent
Expand Down
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
# Changelog

# 1.105.0
* Feature - feat(acs-model): Add LogDriverVolumeConfiguration to ACS Volume shape [#5005](https://github.com/aws/amazon-ecs-agent/pull/5005)
* Enhancement - Removed cloudwatch from windows agent capabilities [#5002](https://github.com/aws/amazon-ecs-agent/pull/5002)
* Enhancement - Update Go version to 1.25.11 [#5003](https://github.com/aws/amazon-ecs-agent/pull/5003)
* Enhancement - Update golang.org/x/net to v0.56.0 [#5004](https://github.com/aws/amazon-ecs-agent/pull/5004)
* Enhancement - Add IsolatedDebugPlatform with DNS field backfill [#4998](https://github.com/aws/amazon-ecs-agent/pull/4998)
* Enhancement - enhancement: Migrate EBS CSI Driver image to AL2023 [#4979](https://github.com/aws/amazon-ecs-agent/pull/4979)
* Enhancement - Bump github.com/aws/aws-sdk-go-v2/config from 1.29.14 to 1.32.23 in /agent [#4997](https://github.com/aws/amazon-ecs-agent/pull/4997)
* Enhancement - Detect cloud-init failure at startup to prevent silent cluster miscon… [#4988](https://github.com/aws/amazon-ecs-agent/pull/4988)
* Enhancement - Add isolated_linux platform for isolated container networking [#4991](https://github.com/aws/amazon-ecs-agent/pull/4991)
* Enhancement - Emit failure metrics from IMDS credentials scanner [#4995](https://github.com/aws/amazon-ecs-agent/pull/4995)
* Enhancement - Update SSM GPG key for ECS Anywhere installation [#4992](https://github.com/aws/amazon-ecs-agent/pull/4992)
* Enhancement - Add Host type and BuildHosts to populate netNS.Hosts [#4985](https://github.com/aws/amazon-ecs-agent/pull/4985)
* Bugfix - Fix TestClientProxy to obtain available port dynamically [#4999](https://github.com/aws/amazon-ecs-agent/pull/4999)
* Bugfix - Fix flaky TestDPEAndACSConnFailureTrackerTracking nil pointer panic [#4993](https://github.com/aws/amazon-ecs-agent/pull/4993)

# 1.104.0
* Feature - Add support for ECS S3 Files integration [#4962](https://github.com/aws/amazon-ecs-agent/pull/4962)
* Feature - Add ECS_PROPAGATE_TASK_MEMORY_LIMIT_CGROUPV2 opt-in config [#4986](https://github.com/aws/amazon-ecs-agent/pull/4986)
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ test: test-ebs-csi
cd ecs-agent && go tool cover -func ../cover.out > ../coverprofile-ecs-agent.out && cd ..

test-init:
cd ecs-init && go test -count=1 -short -v \
cd ecs-init && ${GOTEST} -short -v \
-coverprofile ../cover.out \
-coverpkg=$$(go list ./... | grep -v -f ../scripts/coverfilters/ecs-init-notest-packages.txt | ${COVERPKG_EXCLUDE}) \
./... && cd ..
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.104.0
1.105.0
1 change: 1 addition & 0 deletions agent/engine/serviceconnect/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ type Manager interface {
loader.Loader

GetLoadedImageName() string
GetLoadedImageDigest() string
AugmentTaskContainer(
task *apitask.Task,
container *apicontainer.Container,
Expand Down
55 changes: 51 additions & 4 deletions agent/engine/serviceconnect/manager_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,10 @@ const (
defaultAdminStatsRequest = httpRequestPrefix + "/stats/prometheus?usedonly&filter=metrics_extension&delta"
defaultAdminDrainRequest = httpRequestPrefix + "/drain_listeners?inboundonly"

defaultAgentContainerImageName = "ecs-service-connect-agent"
defaultAgentContainerTagFormat = "interface-%s"
defaultAgentContainerTarballPathFormat = "/managed-agents/serviceconnect/ecs-service-connect-agent.interface-%s.tar"
defaultAgentContainerImageName = "ecs-service-connect-agent"
defaultAgentContainerTagFormat = "interface-%s"
defaultAgentContainerTarballPathFormat = "/managed-agents/serviceconnect/ecs-service-connect-agent.interface-%s.tar"
defaultAgentContainerMetadataPathFormat = "/managed-agents/serviceconnect/ecs-service-connect-agent.interface-%s.metadata.json"

ecsAgentLogFileENV = "ECS_LOGFILE"
defaultECSAgentLogPathContainer = "/log"
Expand Down Expand Up @@ -128,6 +129,7 @@ type manager struct {
agentContainerImageName string
agentContainerTag string
appnetInterfaceVersion string
appnetImageDigest string

ecsClient ecs.ECSClient
containerInstanceARN string
Expand Down Expand Up @@ -181,6 +183,9 @@ func (m *manager) augmentAgentContainer(

task.PopulateServiceConnectRuntimeConfig(config)
container.Image = m.GetLoadedImageName()
if d := m.GetLoadedImageDigest(); d != "" {
container.SetImageDigest(d)
}
return nil
}

Expand Down Expand Up @@ -458,6 +463,38 @@ func (agent *manager) setLoadedAppnetVerion(appnetInterfaceVersion string) {
agent.appnetInterfaceVersion = appnetInterfaceVersion
}

// readMetadataFile is overridden in tests to inject metadata.json contents.
var readMetadataFile = os.ReadFile

// readLoadedImageDigest reads the Appnet agent container image metadata.json
// that ships alongside the tarball and returns the registry manifest digest.
// Returns "" (with a warning logged) if the file is missing or malformed;
// older RPMs do not ship this file, and the Appnet agent container must
// still launch in that case.
func (agent *manager) readLoadedImageDigest(version string) string {
imageMetadataPath := fmt.Sprintf(defaultAgentContainerMetadataPathFormat, version)
raw, err := readMetadataFile(imageMetadataPath)
if err != nil {
logger.Warn("Appnet agent container image metadata unavailable", logger.Fields{
field.Error: err,
"path": imageMetadataPath,
})
return ""
}
var md struct {
SchemaVersion int `json:"schemaVersion"`
Digest string `json:"digest"`
}
if err := json.Unmarshal(raw, &md); err != nil {
logger.Warn("Appnet agent container image metadata malformed", logger.Fields{
field.Error: err,
"path": imageMetadataPath,
})
return ""
}
return md.Digest
}

// LoadImage helps load the AppNetAgent container image for the agent latest supported
// AppNet interface version by looking for the AppNet agent tar name from supported list
// of AppNet versions from highest to lowest version when loading AppNet image
Expand All @@ -480,10 +517,12 @@ func (agent *manager) LoadImage(ctx context.Context, _ *config.Config, dockerCli
continue
}
agent.setLoadedAppnetVerion(supportedAppnetInterfaceVersion)
agent.appnetImageDigest = agent.readLoadedImageDigest(supportedAppnetInterfaceVersion)
imageName := agent.GetLoadedImageName()
logger.Info(fmt.Sprintf("Successfully loaded Appnet agent container tarball: %s", agentContainerTarballPath),
logger.Fields{
field.Image: imageName,
field.Image: imageName,
"appnetImageDigest": agent.appnetImageDigest,
})
return loader.GetContainerImage(imageName, dockerClient)
}
Expand All @@ -499,6 +538,14 @@ func (agent *manager) GetLoadedImageName() string {
return fmt.Sprintf("%s:%s", agent.agentContainerImageName, agent.agentContainerTag)
}

// GetLoadedImageDigest returns the registry manifest digest of the loaded
// Appnet agent container image, sourced from the metadata.json file that
// ships alongside the tarball. Returns "" if the metadata file was missing
// or malformed at LoadImage time; callers must tolerate empty digest.
func (agent *manager) GetLoadedImageDigest() string {
return agent.appnetImageDigest
}

func (agent *manager) GetLoadedAppnetVersion() (string, error) {
return agent.appnetInterfaceVersion, nil
}
Expand Down
102 changes: 102 additions & 0 deletions agent/engine/serviceconnect/manager_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -360,3 +360,105 @@ func TestAugmentTaskContainerError(t *testing.T) {
assert.EqualError(t, namedErr, "instance is IPv6-only but no IPv6 address found for container 'web'")
})
}

// Verifies the metadata.json reader: happy path, missing file, malformed JSON.
// Failures must degrade gracefully (return "" + warn) so older RPMs without
// metadata.json do not break AppNet container launch.
func TestReadLoadedImageDigest(t *testing.T) {
const expectedDigest = "sha256:8bdb42f6a48b7422f5eee379a6522a446850269001e9432cea58296f5fbb1169"

tt := []struct {
name string
readFn func(string) ([]byte, error)
expected string
}{
{
name: "valid metadata.json returns digest",
readFn: func(string) ([]byte, error) {
return []byte(`{"schemaVersion":1,"digest":"` + expectedDigest + `"}`), nil
},
expected: expectedDigest,
},
{
name: "missing file returns empty digest",
readFn: func(string) ([]byte, error) {
return nil, os.ErrNotExist
},
expected: "",
},
{
name: "malformed JSON returns empty digest",
readFn: func(string) ([]byte, error) {
return []byte(`{not valid json`), nil
},
expected: "",
},
{
name: "empty digest field returns empty",
readFn: func(string) ([]byte, error) {
return []byte(`{"schemaVersion":1,"digest":""}`), nil
},
expected: "",
},
}

for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
origRead := readMetadataFile
readMetadataFile = tc.readFn
defer func() { readMetadataFile = origRead }()

m := &manager{}
got := m.readLoadedImageDigest("v1")
assert.Equal(t, tc.expected, got)
})
}
}

// Verifies augmentAgentContainer plumbs the cached digest onto the AppNet
// container via SetImageDigest. The non-empty branch is the load-bearing
// behavior (it makes ImageDigest flow to control plane); the empty branch
// guards against overwriting when an older RPM ships no metadata.json.
func TestAugmentTaskContainerSetsImageDigest(t *testing.T) {
const testDigest = "sha256:8bdb42f6a48b7422f5eee379a6522a446850269001e9432cea58296f5fbb1169"

tt := []struct {
name string
managerDigest string
expectedDigest string
}{
{
name: "manager has digest, container gets it",
managerDigest: testDigest,
expectedDigest: testDigest,
},
{
name: "manager has empty digest, container untouched",
managerDigest: "",
expectedDigest: "",
},
}

for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
origMkdir := mkdirAllAndChown
mkdirAllAndChown = func(path string, perm fs.FileMode, uid, gid int) error {
return nil
}
defer func() { mkdirAllAndChown = origMkdir }()

scTask, _, serviceConnectContainer := getAWSVPCTask(t)

scManager := &manager{
agentContainerImageName: "container_image",
agentContainerTag: "tag",
appnetImageDigest: tc.managerDigest,
}

err := scManager.AugmentTaskContainer(scTask, serviceConnectContainer,
&dockercontainer.HostConfig{}, ipcompatibility.NewIPv4OnlyCompatibility())
assert.NoError(t, err)
assert.Equal(t, tc.expectedDigest, serviceConnectContainer.GetImageDigest())
})
}
}
4 changes: 4 additions & 0 deletions agent/engine/serviceconnect/manager_other.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ func (*manager) GetLoadedImageName() string {
return ""
}

func (*manager) GetLoadedImageDigest() string {
return ""
}

func (*manager) GetLoadedAppnetVersion() (string, error) {
return "", loader.NewUnsupportedPlatformError(fmt.Errorf(
"appnetAgent container get loaded appnet version: unsupported platform: %s/%s",
Expand Down
14 changes: 14 additions & 0 deletions agent/engine/serviceconnect/mock/manager.go

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

Loading
Loading