Skip to content

Commit

Permalink
Merge branch 'dev' into container-ordering feature
Browse files Browse the repository at this point in the history
  • Loading branch information
ubhattacharjya committed Feb 15, 2019
2 parents 2fcf205 + 5c3fa51 commit 680db40
Show file tree
Hide file tree
Showing 12 changed files with 832 additions and 270 deletions.
23 changes: 22 additions & 1 deletion agent/acs/model/api/api-2.json
Original file line number Diff line number Diff line change
Expand Up @@ -203,13 +203,34 @@
"healthCheckType":{"shape":"HealthCheckType"},
"registryAuthentication":{"shape":"RegistryAuthenticationData"},
"logsAuthStrategy":{"shape":"AuthStrategy"},
"secrets":{"shape":"SecretList"}
"secrets":{"shape":"SecretList"},
"dependsOn":{"shape": "DependsOnList"}
}
},
"ContainerList":{
"type":"list",
"member":{"shape":"Container"}
},
"DependsOn":{
"type":"structure",
"members":{
"container":{"shape":"String"},
"condition":{"shape":"ConditionType"}
}
},
"ConditionType":{
"type":"string",
"enum":[
"START",
"COMPLETE",
"SUCCESS",
"HEALTHY"
]
},
"DependsOnList":{
"type":"list",
"member":{"shape":"DependsOn"}
},
"DockerConfig":{
"type":"structure",
"members":{
Expand Down
20 changes: 20 additions & 0 deletions agent/acs/model/ecsacs/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,8 @@ type Container struct {

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

DependsOn []*DependsOn `locationName:"dependsOn" type:"list"`

DockerConfig *DockerConfig `locationName:"dockerConfig" type:"structure"`

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

type DependsOn struct {
_ struct{} `type:"structure"`

Condition *string `locationName:"condition" type:"string" enum:"ConditionType"`

Container *string `locationName:"container" type:"string"`
}

// String returns the string representation
func (s DependsOn) String() string {
return awsutil.Prettify(s)
}

// GoString returns the string representation
func (s DependsOn) GoString() string {
return s.String()
}

type DockerConfig struct {
_ struct{} `type:"structure"`

Expand Down
7 changes: 7 additions & 0 deletions agent/api/container/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ type HealthStatus struct {
type Container struct {
// Name is the name of the container specified in the task definition
Name string
// DependsOn is the field which specifies the ordering for container startup and shutdown.
DependsOn []DependsOn `json:"dependsOn,omitempty"`
// V3EndpointID is a container identifier used to construct v3 metadata endpoint; it's unique among
// all the containers managed by the agent
V3EndpointID string
Expand Down Expand Up @@ -236,6 +238,11 @@ type Container struct {
labels map[string]string
}

type DependsOn struct {
Container string `json:"container"`
Condition string `json:"condition"`
}

// DockerContainer is a mapping between containers-as-docker-knows-them and
// containers-as-we-know-them.
// This is primarily used in DockerState, but lives here such that tasks and
Expand Down
52 changes: 51 additions & 1 deletion agent/api/task/task.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ const (
NvidiaVisibleDevicesEnvVar = "NVIDIA_VISIBLE_DEVICES"
GPUAssociationType = "gpu"

ContainerOrderingStartCondition = "START"
ContainerOrderingRunningCondition = "RUNNING"

arnResourceSections = 2
arnResourceDelimiter = "/"
// networkModeNone specifies the string used to define the `none` docker networking mode
Expand Down Expand Up @@ -250,6 +253,18 @@ func (task *Task) PostUnmarshalTask(cfg *config.Config,
}
}

err := task.initializeContainerOrderingForVolumes()
if err != nil {
seelog.Errorf("Task [%s]: could not initialize volumes dependency for container: %v", task.Arn, err)
return apierrors.NewResourceInitError(task.Arn, err)
}

err = task.initializeContainerOrderingForLinks()
if err != nil {
seelog.Errorf("Task [%s]: could not initialize links dependency for container: %v", task.Arn, err)
return apierrors.NewResourceInitError(task.Arn, err)
}

if task.requiresASMDockerAuthData() {
task.initializeASMAuthResource(credentialsManager, resourceFields)
}
Expand All @@ -262,7 +277,7 @@ func (task *Task) PostUnmarshalTask(cfg *config.Config,
task.initializeASMSecretResource(credentialsManager, resourceFields)
}

err := task.initializeDockerLocalVolumes(dockerClient, ctx)
err = task.initializeDockerLocalVolumes(dockerClient, ctx)
if err != nil {
return apierrors.NewResourceInitError(task.Arn, err)
}
Expand Down Expand Up @@ -1250,6 +1265,41 @@ func (task *Task) shouldOverrideIPCMode(container *apicontainer.Container, docke
}
}

func (task *Task) initializeContainerOrderingForVolumes() error {
for _, container := range task.Containers {
if len(container.VolumesFrom) > 0 {
for _, volume := range container.VolumesFrom {
if _, ok := task.ContainerByName(volume.SourceContainer); !ok {
return fmt.Errorf("could not find container with name %s", volume.SourceContainer)
}
dependOn := apicontainer.DependsOn{Container: volume.SourceContainer, Condition: ContainerOrderingStartCondition}
container.DependsOn = append(container.DependsOn, dependOn)
}
}
}
return nil
}

func (task *Task) initializeContainerOrderingForLinks() error {
for _, container := range task.Containers {
if len(container.Links) > 0 {
for _, link := range container.Links {
linkParts := strings.Split(link, ":")
if len(linkParts) > 2 {
return fmt.Errorf("Invalid link format")
}
linkName := linkParts[0]
if _, ok := task.ContainerByName(linkName); !ok {
return fmt.Errorf("could not find container with name %s", linkName)
}
dependOn := apicontainer.DependsOn{Container: linkName, Condition: ContainerOrderingRunningCondition}
container.DependsOn = append(container.DependsOn, dependOn)
}
}
}
return nil
}

func (task *Task) dockerLinks(container *apicontainer.Container, dockerContainerMap map[string]*apicontainer.DockerContainer) ([]string, error) {
dockerLinkArr := make([]string, len(container.Links))
for i, link := range container.Links {
Expand Down
97 changes: 97 additions & 0 deletions agent/api/task/task_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2848,3 +2848,100 @@ func TestTaskGPUDisabled(t *testing.T) {

assert.False(t, testTask.isGPUEnabled())
}

func TestInitializeContainerOrderingWithLinksAndVolumesFrom(t *testing.T) {
containerWithOnlyVolume := &apicontainer.Container{
Name: "myName",
Image: "image:tag",
VolumesFrom: []apicontainer.VolumeFrom{{SourceContainer: "myName1"}},
}

containerWithOnlyLink := &apicontainer.Container{
Name: "myName1",
Image: "image:tag",
Links: []string{"myName"},
}

containerWithBothVolumeAndLink := &apicontainer.Container{
Name: "myName2",
Image: "image:tag",
VolumesFrom: []apicontainer.VolumeFrom{{SourceContainer: "myName"}},
Links: []string{"myName1"},
}

containerWithNoVolumeOrLink := &apicontainer.Container{
Name: "myName3",
Image: "image:tag",
}

task := &Task{
Arn: "test",
ResourcesMapUnsafe: make(map[string][]taskresource.TaskResource),
Containers: []*apicontainer.Container{containerWithOnlyVolume, containerWithOnlyLink,
containerWithBothVolumeAndLink, containerWithNoVolumeOrLink},
}

err := task.initializeContainerOrderingForVolumes()
assert.NoError(t, err)
err = task.initializeContainerOrderingForLinks()
assert.NoError(t, err)

containerResultWithVolume := task.Containers[0]
assert.Equal(t, "myName1", containerResultWithVolume.DependsOn[0].Container)
assert.Equal(t, ContainerOrderingStartCondition, containerResultWithVolume.DependsOn[0].Condition)

containerResultWithLink := task.Containers[1]
assert.Equal(t, "myName", containerResultWithLink.DependsOn[0].Container)
assert.Equal(t, ContainerOrderingRunningCondition, containerResultWithLink.DependsOn[0].Condition)

containerResultWithBothVolumeAndLink := task.Containers[2]
assert.Equal(t, "myName", containerResultWithBothVolumeAndLink.DependsOn[0].Container)
assert.Equal(t, ContainerOrderingStartCondition, containerResultWithBothVolumeAndLink.DependsOn[0].Condition)
assert.Equal(t, "myName1", containerResultWithBothVolumeAndLink.DependsOn[1].Container)
assert.Equal(t, ContainerOrderingRunningCondition, containerResultWithBothVolumeAndLink.DependsOn[1].Condition)

containerResultWithNoVolumeOrLink := task.Containers[3]
assert.Equal(t, 0, len(containerResultWithNoVolumeOrLink.DependsOn))
}

func TestInitializeContainerOrderingWithError(t *testing.T) {
containerWithVolumeError := &apicontainer.Container{
Name: "myName",
Image: "image:tag",
VolumesFrom: []apicontainer.VolumeFrom{{SourceContainer: "dummyContainer"}},
}

containerWithLinkError1 := &apicontainer.Container{
Name: "myName1",
Image: "image:tag",
Links: []string{"dummyContainer"},
}

containerWithLinkError2 := &apicontainer.Container{
Name: "myName2",
Image: "image:tag",
Links: []string{"myName:link1:link2"},
}

task1 := &Task{
Arn: "test",
ResourcesMapUnsafe: make(map[string][]taskresource.TaskResource),
Containers: []*apicontainer.Container{containerWithVolumeError, containerWithLinkError1},
}

task2 := &Task{
Arn: "test",
ResourcesMapUnsafe: make(map[string][]taskresource.TaskResource),
Containers: []*apicontainer.Container{containerWithVolumeError, containerWithLinkError2},
}

errVolume1 := task1.initializeContainerOrderingForVolumes()
assert.Error(t, errVolume1)
errLink1 := task1.initializeContainerOrderingForLinks()
assert.Error(t, errLink1)

errVolume2 := task2.initializeContainerOrderingForVolumes()
assert.Error(t, errVolume2)
errLink2 := task2.initializeContainerOrderingForLinks()
assert.Error(t, errLink2)
}
4 changes: 4 additions & 0 deletions agent/app/agent_capability.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ const (
capabiltyPIDAndIPCNamespaceSharing = "pid-ipc-namespace-sharing"
capabilityNvidiaDriverVersionInfix = "nvidia-driver-version."
capabilityECREndpoint = "ecr-endpoint"
capabilityContainerOrdering = "container-ordering"
taskEIAAttributeSuffix = "task-eia"
)

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

// support container ordering in agent
capabilities = appendNameOnlyAttribute(capabilities, attributePrefix+capabilityContainerOrdering)

return capabilities, nil
}

Expand Down
3 changes: 3 additions & 0 deletions agent/app/agent_capability_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ func TestCapabilities(t *testing.T) {
{
Name: aws.String(attributePrefix + taskEIAAttributeSuffix),
},
{
Name: aws.String(attributePrefix + capabilityContainerOrdering),
},
}...)

ctx, cancel := context.WithCancel(context.TODO())
Expand Down
Loading

0 comments on commit 680db40

Please sign in to comment.