Skip to content

Commit 3a3ce18

Browse files
author
Ben Cordero
committed
Add Private Host IPv4 address to container metadata
Related issue: #1575 Related PR: #1730 PR 1730 adds the Public IP Address of the host to the container metadata file, however the EC2 host may be configured without a public address. In this case, the EC2 metadata API returns a 404 response, and the host IP is not available to containers. Example ECS Agent Log ``` [ERROR] Unable to retrieve Host Instance PublicIPv4 Address: EC2MetadataError: failed to make EC2Metadata request caused by: <?xml version="1.0" encoding="iso-8859-1"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <title>404 - Not Found</title> </head> <body> <h1>404 - Not Found</h1> </body> </html> ``` This commit adds an extra field to the container metadata json, `HostPrivateIPv4Address` which is available on EC2 hosts without a public address.
1 parent aabe65e commit 3a3ce18

14 files changed

+186
-40
lines changed

agent/app/agent.go

+14-1
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,7 @@ func (agent *ecsAgent) doStart(containerChangeEventStream *eventstream.EventStre
297297
if agent.cfg.ContainerMetadataEnabled {
298298
agent.metadataManager.SetContainerInstanceARN(agent.containerInstanceARN)
299299
agent.metadataManager.SetAvailabilityZone(agent.availabilityZone)
300+
agent.metadataManager.SetHostPrivateIPv4Address(agent.getHostPrivateIPv4AddressFromEC2Metadata())
300301
agent.metadataManager.SetHostPublicIPv4Address(agent.getHostPublicIPv4AddressFromEC2Metadata())
301302
}
302303

@@ -695,10 +696,22 @@ func mergeTags(localTags []*ecs.Tag, ec2Tags []*ecs.Tag) []*ecs.Tag {
695696
return utils.MapToTags(tagsMap)
696697
}
697698

699+
// getHostPrivateIPv4AddressFromEC2Metadata will retrieve the PrivateIPAddress (IPv4) of this
700+
// instance throught the EC2 API
701+
func (agent *ecsAgent) getHostPrivateIPv4AddressFromEC2Metadata() string {
702+
// Get instance private IP from ec2 metadata client.
703+
hostPrivateIPv4Address, err := agent.ec2MetadataClient.PrivateIPv4Address()
704+
if err != nil {
705+
seelog.Errorf("Unable to retrieve Host Instance PrivateIPv4 Address: %v", err)
706+
return ""
707+
}
708+
return hostPrivateIPv4Address
709+
}
710+
698711
// getHostPublicIPv4AddressFromEC2Metadata will retrieve the PublicIPAddress (IPv4) of this
699712
// instance through the EC2 API
700713
func (agent *ecsAgent) getHostPublicIPv4AddressFromEC2Metadata() string {
701-
// Get instance ID from ec2 metadata client.
714+
// Get instance public IP from ec2 metadata client.
702715
hostPublicIPv4Address, err := agent.ec2MetadataClient.PublicIPv4Address()
703716
if err != nil {
704717
seelog.Errorf("Unable to retrieve Host Instance PublicIPv4 Address: %v", err)

agent/app/agent_test.go

+39-4
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,11 @@ import (
5151
)
5252

5353
const (
54-
clusterName = "some-cluster"
55-
containerInstanceARN = "container-instance1"
56-
availabilityZone = "us-west-2b"
57-
hostPublicIPv4Address = "127.0.0.1"
54+
clusterName = "some-cluster"
55+
containerInstanceARN = "container-instance1"
56+
availabilityZone = "us-west-2b"
57+
hostPrivateIPv4Address = "127.0.0.1"
58+
hostPublicIPv4Address = "127.0.0.1"
5859
)
5960

6061
var apiVersions = []dockerclient.DockerVersion{
@@ -304,6 +305,7 @@ func TestDoStartRegisterAvailabilityZone(t *testing.T) {
304305
defer ctrl.Finish()
305306

306307
ec2MetadataClient := mock_ec2.NewMockEC2MetadataClient(ctrl)
308+
ec2MetadataClient.EXPECT().PrivateIPv4Address().Return(hostPrivateIPv4Address, nil)
307309
ec2MetadataClient.EXPECT().PublicIPv4Address().Return(hostPublicIPv4Address, nil)
308310

309311
var discoverEndpointsInvoked sync.WaitGroup
@@ -339,6 +341,7 @@ func TestDoStartRegisterAvailabilityZone(t *testing.T) {
339341
"arn:123", availabilityZone, nil),
340342
containermetadata.EXPECT().SetContainerInstanceARN("arn:123"),
341343
containermetadata.EXPECT().SetAvailabilityZone(availabilityZone),
344+
containermetadata.EXPECT().SetHostPrivateIPv4Address(hostPrivateIPv4Address),
342345
containermetadata.EXPECT().SetHostPublicIPv4Address(hostPublicIPv4Address),
343346
imageManager.EXPECT().SetSaver(gomock.Any()),
344347
dockerClient.EXPECT().ContainerEvents(gomock.Any()),
@@ -1115,6 +1118,38 @@ func TestGetContainerInstanceTagsFromEC2APIFailToDescribeECSTagsForInstance(t *t
11151118
assert.Nil(t, resTags)
11161119
}
11171120

1121+
func TestGetHostPrivateIPv4AddressFromEC2Metadata(t *testing.T) {
1122+
ctrl := gomock.NetController(t)
1123+
defer ctrl.Finish()
1124+
1125+
ec2MetadataClient := mock_ec2.NewMockEC2MetadataClient(ctrl)
1126+
ec2Client := mock_ec2.NewMockClient(ctrl)
1127+
1128+
agent := &ecsAgent{
1129+
ec2MetadataClient: ec2MetadataClient,
1130+
ec2Client: ec2Client,
1131+
}
1132+
ec2MetadataClient.EXPECT().PrivateIPv4Address().Return(hostPrivateIPv4Address, nil)
1133+
1134+
assert.Equal(t, hostPrivateIPv4Address, agent.getHostPrivateIPv4AddressFromEC2Metadata())
1135+
}
1136+
1137+
func TestGetHostPrivateIPv4AddressFromEC2MetadataFailWithError(t *testing.T) {
1138+
ctrl := gomock.NewController(t)
1139+
defer ctrl.Finish()
1140+
1141+
ec2MetadataClient := mock_ec2.NewMockEC2MetadataClient(ctrl)
1142+
ec2Client := mock_ec2.NewMockClient(ctrl)
1143+
1144+
agent := &ecsAgent{
1145+
ec2MetadataClient: ec2MetadataClient,
1146+
ec2Client: ec2Client,
1147+
}
1148+
ec2MetadataClient.EXPECT().PrivateIPv4Address().Return("", errors.New("Unable to get IP Address"))
1149+
1150+
assert.Empty(t, agent.getHostPrivateIPv4AddressFromEC2Metadata())
1151+
}
1152+
11181153
func TestGetHostPublicIPv4AddressFromEC2Metadata(t *testing.T) {
11191154
ctrl := gomock.NewController(t)
11201155
defer ctrl.Finish()

agent/containermetadata/manager.go

+10-1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ const (
4040
type Manager interface {
4141
SetContainerInstanceARN(string)
4242
SetAvailabilityZone(string)
43+
SetHostPrivateIPv4Address(string)
4344
SetHostPublicIPv4Address(string)
4445
Create(*dockercontainer.Config, *dockercontainer.HostConfig, *apitask.Task, string) error
4546
Update(context.Context, string, *apitask.Task, string) error
@@ -67,7 +68,9 @@ type metadataManager struct {
6768
ioutilWrap ioutilwrapper.IOUtil
6869
// availabilityZone is the availabiltyZone where task is in
6970
availabilityZone string
70-
// hostPublicIPv4Address is the public IPv4 address associated with the EC2 instance ID
71+
// hostPrivateIPv4Address is the private IPv4 address associated with the EC2 instance
72+
hostPrivateIPv4Address string
73+
// hostPublicIPv4Address is the public IPv4 address associated with the EC2 instance
7174
hostPublicIPv4Address string
7275
}
7376

@@ -95,6 +98,12 @@ func (manager *metadataManager) SetAvailabilityZone(availabilityZone string) {
9598
manager.availabilityZone = availabilityZone
9699
}
97100

101+
// SetHostPrivateIPv4Address sets the metadataManager's hostPrivateIPv4Address which is not available
102+
// at its creation as this information is not present immediately at the agent's startup
103+
func (manager *metadataManager) SetHostPrivateIPv4Address(ipv4address string) {
104+
manager.hostPrivateIPv4Address = ipv4address
105+
}
106+
98107
// SetHostPublicIPv4Address sets the metadataManager's hostPublicIPv4Address which is not available
99108
// at its creation as this information is not present immediately at the agent's startup
100109
func (manager *metadataManager) SetHostPublicIPv4Address(ipv4address string) {

agent/containermetadata/manager_test.go

+10
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ const (
4141
containerName = "container"
4242
dataDir = "ecs_mockdata"
4343
availabilityZone = "us-west-2b"
44+
hostPrivateIPv4Address = "127.0.0.1"
4445
hostPublicIPv4Address = "127.0.0.1"
4546
)
4647

@@ -75,6 +76,15 @@ func TestSetAvailabilityZone(t *testing.T) {
7576
assert.Equal(t, mockAvailabilityZone, newManager.availabilityZone)
7677
}
7778

79+
// TestSetHostPrivateIPv4Address checks whether the container hostPublicIPv4Address is set correctly.
80+
func TestSetHostPrivateIPv4Address(t *testing.T) {
81+
_, _, _, _, done := managerSetup(t)
82+
defer done()
83+
newManager := &metadataManager{}
84+
newManager.SetHostPrivateIPv4Address(hostPublicIPv4Address)
85+
assert.Equal(t, hostPrivateIPv4Address, newManager.hostPrivateIPv4Address)
86+
}
87+
7888
// TestSetHostPublicIPv4Address checks whether the container hostPublicIPv4Address is set correctly.
7989
func TestSetHostPublicIPv4Address(t *testing.T) {
8090
_, _, _, _, done := managerSetup(t)

agent/containermetadata/mocks/containermetadata_mocks.go

+10
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

agent/containermetadata/parse_metadata.go

+6-4
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,11 @@ func (manager *metadataManager) parseMetadataAtContainerCreate(task *apitask.Tas
3838
taskDefinitionFamily: task.Family,
3939
taskDefinitionRevision: task.Version,
4040
},
41-
containerInstanceARN: manager.containerInstanceARN,
42-
metadataStatus: MetadataInitial,
43-
availabilityZone: manager.availabilityZone,
44-
hostPublicIPv4Address: manager.hostPublicIPv4Address,
41+
containerInstanceARN: manager.containerInstanceARN,
42+
metadataStatus: MetadataInitial,
43+
availabilityZone: manager.availabilityZone,
44+
hostPrivateIPv4Address: manager.hostPrivateIPv4Address,
45+
hostPublicIPv4Address: manager.hostPublicIPv4Address,
4546
}
4647
}
4748

@@ -63,6 +64,7 @@ func (manager *metadataManager) parseMetadata(dockerContainer *types.ContainerJS
6364
containerInstanceARN: manager.containerInstanceARN,
6465
metadataStatus: MetadataReady,
6566
availabilityZone: manager.availabilityZone,
67+
hostPrivateIPv4Address: manager.hostPrivateIPv4Address,
6668
hostPublicIPv4Address: manager.hostPublicIPv4Address,
6769
}
6870
}

0 commit comments

Comments
 (0)