Skip to content

Commit 14b5e40

Browse files
Merge branch 'container-ordering-feature' into dev
2 parents 6c02fa8 + 5c3fa51 commit 14b5e40

File tree

12 files changed

+832
-270
lines changed

12 files changed

+832
-270
lines changed

agent/acs/model/api/api-2.json

+22-1
Original file line numberDiff line numberDiff line change
@@ -203,13 +203,34 @@
203203
"healthCheckType":{"shape":"HealthCheckType"},
204204
"registryAuthentication":{"shape":"RegistryAuthenticationData"},
205205
"logsAuthStrategy":{"shape":"AuthStrategy"},
206-
"secrets":{"shape":"SecretList"}
206+
"secrets":{"shape":"SecretList"},
207+
"dependsOn":{"shape": "DependsOnList"}
207208
}
208209
},
209210
"ContainerList":{
210211
"type":"list",
211212
"member":{"shape":"Container"}
212213
},
214+
"DependsOn":{
215+
"type":"structure",
216+
"members":{
217+
"container":{"shape":"String"},
218+
"condition":{"shape":"ConditionType"}
219+
}
220+
},
221+
"ConditionType":{
222+
"type":"string",
223+
"enum":[
224+
"START",
225+
"COMPLETE",
226+
"SUCCESS",
227+
"HEALTHY"
228+
]
229+
},
230+
"DependsOnList":{
231+
"type":"list",
232+
"member":{"shape":"DependsOn"}
233+
},
213234
"DockerConfig":{
214235
"type":"structure",
215236
"members":{

agent/acs/model/ecsacs/api.go

+20
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,8 @@ type Container struct {
206206

207207
Cpu *int64 `locationName:"cpu" type:"integer"`
208208

209+
DependsOn []*DependsOn `locationName:"dependsOn" type:"list"`
210+
209211
DockerConfig *DockerConfig `locationName:"dockerConfig" type:"structure"`
210212

211213
EntryPoint []*string `locationName:"entryPoint" type:"list"`
@@ -249,6 +251,24 @@ func (s Container) GoString() string {
249251
return s.String()
250252
}
251253

254+
type DependsOn struct {
255+
_ struct{} `type:"structure"`
256+
257+
Condition *string `locationName:"condition" type:"string" enum:"ConditionType"`
258+
259+
Container *string `locationName:"container" type:"string"`
260+
}
261+
262+
// String returns the string representation
263+
func (s DependsOn) String() string {
264+
return awsutil.Prettify(s)
265+
}
266+
267+
// GoString returns the string representation
268+
func (s DependsOn) GoString() string {
269+
return s.String()
270+
}
271+
252272
type DockerConfig struct {
253273
_ struct{} `type:"structure"`
254274

agent/api/container/container.go

+7
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ type HealthStatus struct {
9393
type Container struct {
9494
// Name is the name of the container specified in the task definition
9595
Name string
96+
// DependsOn is the field which specifies the ordering for container startup and shutdown.
97+
DependsOn []DependsOn `json:"dependsOn,omitempty"`
9698
// V3EndpointID is a container identifier used to construct v3 metadata endpoint; it's unique among
9799
// all the containers managed by the agent
98100
V3EndpointID string
@@ -236,6 +238,11 @@ type Container struct {
236238
labels map[string]string
237239
}
238240

241+
type DependsOn struct {
242+
Container string `json:"container"`
243+
Condition string `json:"condition"`
244+
}
245+
239246
// DockerContainer is a mapping between containers-as-docker-knows-them and
240247
// containers-as-we-know-them.
241248
// This is primarily used in DockerState, but lives here such that tasks and

agent/api/task/task.go

+51-1
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ const (
7272
NvidiaVisibleDevicesEnvVar = "NVIDIA_VISIBLE_DEVICES"
7373
GPUAssociationType = "gpu"
7474

75+
ContainerOrderingStartCondition = "START"
76+
ContainerOrderingRunningCondition = "RUNNING"
77+
7578
arnResourceSections = 2
7679
arnResourceDelimiter = "/"
7780
// networkModeNone specifies the string used to define the `none` docker networking mode
@@ -250,6 +253,18 @@ func (task *Task) PostUnmarshalTask(cfg *config.Config,
250253
}
251254
}
252255

256+
err := task.initializeContainerOrderingForVolumes()
257+
if err != nil {
258+
seelog.Errorf("Task [%s]: could not initialize volumes dependency for container: %v", task.Arn, err)
259+
return apierrors.NewResourceInitError(task.Arn, err)
260+
}
261+
262+
err = task.initializeContainerOrderingForLinks()
263+
if err != nil {
264+
seelog.Errorf("Task [%s]: could not initialize links dependency for container: %v", task.Arn, err)
265+
return apierrors.NewResourceInitError(task.Arn, err)
266+
}
267+
253268
if task.requiresASMDockerAuthData() {
254269
task.initializeASMAuthResource(credentialsManager, resourceFields)
255270
}
@@ -262,7 +277,7 @@ func (task *Task) PostUnmarshalTask(cfg *config.Config,
262277
task.initializeASMSecretResource(credentialsManager, resourceFields)
263278
}
264279

265-
err := task.initializeDockerLocalVolumes(dockerClient, ctx)
280+
err = task.initializeDockerLocalVolumes(dockerClient, ctx)
266281
if err != nil {
267282
return apierrors.NewResourceInitError(task.Arn, err)
268283
}
@@ -1250,6 +1265,41 @@ func (task *Task) shouldOverrideIPCMode(container *apicontainer.Container, docke
12501265
}
12511266
}
12521267

1268+
func (task *Task) initializeContainerOrderingForVolumes() error {
1269+
for _, container := range task.Containers {
1270+
if len(container.VolumesFrom) > 0 {
1271+
for _, volume := range container.VolumesFrom {
1272+
if _, ok := task.ContainerByName(volume.SourceContainer); !ok {
1273+
return fmt.Errorf("could not find container with name %s", volume.SourceContainer)
1274+
}
1275+
dependOn := apicontainer.DependsOn{Container: volume.SourceContainer, Condition: ContainerOrderingStartCondition}
1276+
container.DependsOn = append(container.DependsOn, dependOn)
1277+
}
1278+
}
1279+
}
1280+
return nil
1281+
}
1282+
1283+
func (task *Task) initializeContainerOrderingForLinks() error {
1284+
for _, container := range task.Containers {
1285+
if len(container.Links) > 0 {
1286+
for _, link := range container.Links {
1287+
linkParts := strings.Split(link, ":")
1288+
if len(linkParts) > 2 {
1289+
return fmt.Errorf("Invalid link format")
1290+
}
1291+
linkName := linkParts[0]
1292+
if _, ok := task.ContainerByName(linkName); !ok {
1293+
return fmt.Errorf("could not find container with name %s", linkName)
1294+
}
1295+
dependOn := apicontainer.DependsOn{Container: linkName, Condition: ContainerOrderingRunningCondition}
1296+
container.DependsOn = append(container.DependsOn, dependOn)
1297+
}
1298+
}
1299+
}
1300+
return nil
1301+
}
1302+
12531303
func (task *Task) dockerLinks(container *apicontainer.Container, dockerContainerMap map[string]*apicontainer.DockerContainer) ([]string, error) {
12541304
dockerLinkArr := make([]string, len(container.Links))
12551305
for i, link := range container.Links {

agent/api/task/task_test.go

+97
Original file line numberDiff line numberDiff line change
@@ -2848,3 +2848,100 @@ func TestTaskGPUDisabled(t *testing.T) {
28482848

28492849
assert.False(t, testTask.isGPUEnabled())
28502850
}
2851+
2852+
func TestInitializeContainerOrderingWithLinksAndVolumesFrom(t *testing.T) {
2853+
containerWithOnlyVolume := &apicontainer.Container{
2854+
Name: "myName",
2855+
Image: "image:tag",
2856+
VolumesFrom: []apicontainer.VolumeFrom{{SourceContainer: "myName1"}},
2857+
}
2858+
2859+
containerWithOnlyLink := &apicontainer.Container{
2860+
Name: "myName1",
2861+
Image: "image:tag",
2862+
Links: []string{"myName"},
2863+
}
2864+
2865+
containerWithBothVolumeAndLink := &apicontainer.Container{
2866+
Name: "myName2",
2867+
Image: "image:tag",
2868+
VolumesFrom: []apicontainer.VolumeFrom{{SourceContainer: "myName"}},
2869+
Links: []string{"myName1"},
2870+
}
2871+
2872+
containerWithNoVolumeOrLink := &apicontainer.Container{
2873+
Name: "myName3",
2874+
Image: "image:tag",
2875+
}
2876+
2877+
task := &Task{
2878+
Arn: "test",
2879+
ResourcesMapUnsafe: make(map[string][]taskresource.TaskResource),
2880+
Containers: []*apicontainer.Container{containerWithOnlyVolume, containerWithOnlyLink,
2881+
containerWithBothVolumeAndLink, containerWithNoVolumeOrLink},
2882+
}
2883+
2884+
err := task.initializeContainerOrderingForVolumes()
2885+
assert.NoError(t, err)
2886+
err = task.initializeContainerOrderingForLinks()
2887+
assert.NoError(t, err)
2888+
2889+
containerResultWithVolume := task.Containers[0]
2890+
assert.Equal(t, "myName1", containerResultWithVolume.DependsOn[0].Container)
2891+
assert.Equal(t, ContainerOrderingStartCondition, containerResultWithVolume.DependsOn[0].Condition)
2892+
2893+
containerResultWithLink := task.Containers[1]
2894+
assert.Equal(t, "myName", containerResultWithLink.DependsOn[0].Container)
2895+
assert.Equal(t, ContainerOrderingRunningCondition, containerResultWithLink.DependsOn[0].Condition)
2896+
2897+
containerResultWithBothVolumeAndLink := task.Containers[2]
2898+
assert.Equal(t, "myName", containerResultWithBothVolumeAndLink.DependsOn[0].Container)
2899+
assert.Equal(t, ContainerOrderingStartCondition, containerResultWithBothVolumeAndLink.DependsOn[0].Condition)
2900+
assert.Equal(t, "myName1", containerResultWithBothVolumeAndLink.DependsOn[1].Container)
2901+
assert.Equal(t, ContainerOrderingRunningCondition, containerResultWithBothVolumeAndLink.DependsOn[1].Condition)
2902+
2903+
containerResultWithNoVolumeOrLink := task.Containers[3]
2904+
assert.Equal(t, 0, len(containerResultWithNoVolumeOrLink.DependsOn))
2905+
}
2906+
2907+
func TestInitializeContainerOrderingWithError(t *testing.T) {
2908+
containerWithVolumeError := &apicontainer.Container{
2909+
Name: "myName",
2910+
Image: "image:tag",
2911+
VolumesFrom: []apicontainer.VolumeFrom{{SourceContainer: "dummyContainer"}},
2912+
}
2913+
2914+
containerWithLinkError1 := &apicontainer.Container{
2915+
Name: "myName1",
2916+
Image: "image:tag",
2917+
Links: []string{"dummyContainer"},
2918+
}
2919+
2920+
containerWithLinkError2 := &apicontainer.Container{
2921+
Name: "myName2",
2922+
Image: "image:tag",
2923+
Links: []string{"myName:link1:link2"},
2924+
}
2925+
2926+
task1 := &Task{
2927+
Arn: "test",
2928+
ResourcesMapUnsafe: make(map[string][]taskresource.TaskResource),
2929+
Containers: []*apicontainer.Container{containerWithVolumeError, containerWithLinkError1},
2930+
}
2931+
2932+
task2 := &Task{
2933+
Arn: "test",
2934+
ResourcesMapUnsafe: make(map[string][]taskresource.TaskResource),
2935+
Containers: []*apicontainer.Container{containerWithVolumeError, containerWithLinkError2},
2936+
}
2937+
2938+
errVolume1 := task1.initializeContainerOrderingForVolumes()
2939+
assert.Error(t, errVolume1)
2940+
errLink1 := task1.initializeContainerOrderingForLinks()
2941+
assert.Error(t, errLink1)
2942+
2943+
errVolume2 := task2.initializeContainerOrderingForVolumes()
2944+
assert.Error(t, errVolume2)
2945+
errLink2 := task2.initializeContainerOrderingForLinks()
2946+
assert.Error(t, errLink2)
2947+
}

agent/app/agent_capability.go

+4
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ const (
4242
capabiltyPIDAndIPCNamespaceSharing = "pid-ipc-namespace-sharing"
4343
capabilityNvidiaDriverVersionInfix = "nvidia-driver-version."
4444
capabilityECREndpoint = "ecr-endpoint"
45+
capabilityContainerOrdering = "container-ordering"
4546
taskEIAAttributeSuffix = "task-eia"
4647
)
4748

@@ -141,6 +142,9 @@ func (agent *ecsAgent) capabilities() ([]*ecs.Attribute, error) {
141142
// support elastic inference in agent
142143
capabilities = appendNameOnlyAttribute(capabilities, attributePrefix+taskEIAAttributeSuffix)
143144

145+
// support container ordering in agent
146+
capabilities = appendNameOnlyAttribute(capabilities, attributePrefix+capabilityContainerOrdering)
147+
144148
return capabilities, nil
145149
}
146150

agent/app/agent_capability_test.go

+3
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,9 @@ func TestCapabilities(t *testing.T) {
126126
{
127127
Name: aws.String(attributePrefix + taskEIAAttributeSuffix),
128128
},
129+
{
130+
Name: aws.String(attributePrefix + capabilityContainerOrdering),
131+
},
129132
}...)
130133

131134
ctx, cancel := context.WithCancel(context.TODO())

0 commit comments

Comments
 (0)