Skip to content

Commit

Permalink
Update ManagedDaemon and Model
Browse files Browse the repository at this point in the history
  • Loading branch information
fierlion committed Sep 18, 2023
1 parent 21bd233 commit 106d15b
Show file tree
Hide file tree
Showing 10 changed files with 533 additions and 35 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ _bin/
*.swp
*.orig
/agent/version/_version.go
/ecs-agent/daemonimages/csidriver/tarfiles
/ecs-init/version/version.go
.agignore
*.sublime-*
Expand Down
31 changes: 27 additions & 4 deletions agent/engine/daemonmanager/daemon_manager_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/docker/docker/api/types"
dockercontainer "github.com/docker/docker/api/types/container"
dockermount "github.com/docker/docker/api/types/mount"
"github.com/pborman/uuid"
)

Expand All @@ -55,24 +56,46 @@ func (dm *daemonManager) CreateDaemonTask() (*apitask.Task, error) {
imageName := dm.managedDaemon.GetImageName()
loadedImageRef := dm.managedDaemon.GetLoadedDaemonImageRef()
containerRunning := apicontainerstatus.ContainerRunning
stringCaps := []string{}
if dm.managedDaemon.GetLinuxParameters() != nil {
caps := dm.managedDaemon.GetLinuxParameters().Capabilities.Add
for _, cap := range caps {
stringCaps = append(stringCaps, *cap)
}
}
dockerHostConfig := dockercontainer.HostConfig{
Mounts: []dockermount.Mount{},
NetworkMode: apitask.HostNetworkMode,
// the default value of 0 for MaximumRetryCount means retry indefinitely
RestartPolicy: dockercontainer.RestartPolicy{
Name: "on-failure",
MaximumRetryCount: 0,
},
Privileged: dm.managedDaemon.GetPrivileged(),
CapAdd: stringCaps,
}
if !dm.managedDaemon.IsValidManagedDaemon() {
return nil, fmt.Errorf("%s is an invalid managed daemon", imageName)
}
for _, mount := range dm.managedDaemon.GetMountPoints() {
err := mkdirAllAndChown(mount.SourceVolumeHostPath, daemonMountPermission, daemonUID, os.Getegid())

for _, mp := range dm.managedDaemon.GetMountPoints() {
err := mkdirAllAndChown(mp.SourceVolumeHostPath, daemonMountPermission, daemonUID, os.Getegid())
if err != nil {
return nil, err
}
dockerHostConfig.Binds = append(dockerHostConfig.Binds,
fmt.Sprintf("%s:%s", mount.SourceVolumeHostPath, mount.ContainerPath))
var bindOptions = dockermount.BindOptions{}

if mp.PropagationShared {
// https://github.com/moby/moby/blob/master/api/types/mount/mount.go#L52
bindOptions.Propagation = dockermount.PropagationShared
}
mountPoint := dockermount.Mount{
Type: dockermount.TypeBind,
Source: mp.SourceVolumeHostPath,
Target: mp.ContainerPath,
BindOptions: &bindOptions,
}
dockerHostConfig.Mounts = append(dockerHostConfig.Mounts, mountPoint)
}
rawHostConfig, err := json.Marshal(&dockerHostConfig)
if err != nil {
Expand Down
94 changes: 84 additions & 10 deletions agent/engine/daemonmanager/daemon_manager_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"encoding/json"
"fmt"
"io/fs"
"strings"
"testing"
"time"

Expand Down Expand Up @@ -54,28 +55,28 @@ func TestCreateDaemonTask(t *testing.T) {
testName: "Basic Daemon",
testDaemonName: TestDaemonName,
testImageRef: TestImageRef,
testOtherMount: &md.MountPoint{SourceVolumeID: TestOtherVolumeID, ContainerPath: "/container/other/", SourceVolumeHostPath: "/var/ecs/other/"},
testOtherMount: &md.MountPoint{SourceVolumeID: TestOtherVolumeID, ContainerPath: "/container/other/", SourceVolumeHostPath: "/var/ecs/other/", PropagationShared: false},
testHealthCheck: []string{TestHealthString},
},
{
testName: "Daemon Updated Daemon Name",
testDaemonName: "TestDeemen",
testImageRef: TestImageRef,
testOtherMount: &md.MountPoint{SourceVolumeID: TestOtherVolumeID, ContainerPath: "/container/other/", SourceVolumeHostPath: "/var/ecs/other/"},
testOtherMount: &md.MountPoint{SourceVolumeID: TestOtherVolumeID, ContainerPath: "/container/other/", SourceVolumeHostPath: "/var/ecs/other/", PropagationShared: false},
testHealthCheck: []string{TestHealthString},
},
{
testName: "Daemon Updated Image ref",
testDaemonName: TestDaemonName,
testImageRef: "TestOtherImage",
testOtherMount: &md.MountPoint{SourceVolumeID: TestOtherVolumeID, ContainerPath: "/container/other/", SourceVolumeHostPath: "/var/ecs/other/"},
testOtherMount: &md.MountPoint{SourceVolumeID: TestOtherVolumeID, ContainerPath: "/container/other/", SourceVolumeHostPath: "/var/ecs/other/", PropagationShared: false},
testHealthCheck: []string{TestHealthString},
},
{
testName: "Daemon With Updated Mounts",
testDaemonName: TestDaemonName,
testImageRef: TestImageRef,
testOtherMount: &md.MountPoint{SourceVolumeID: "testUpdatedMountVolume", ContainerPath: "/container/other/", SourceVolumeHostPath: "/var/ecs/other/"},
testOtherMount: &md.MountPoint{SourceVolumeID: "testUpdatedMountVolume", ContainerPath: "/container/other/", SourceVolumeHostPath: "/var/ecs/other/", PropagationShared: false},
testHealthCheck: []string{TestHealthString},
},
{
Expand Down Expand Up @@ -124,9 +125,12 @@ func TestCreateDaemonTask(t *testing.T) {
var hostConfigMap map[string]interface{}
json.Unmarshal([]byte(aws.StringValue(configRaw)), &configMap)
json.Unmarshal([]byte(aws.StringValue(hostConfigRaw)), &hostConfigMap)
// validate mount points
containerBinds := hostConfigMap["Binds"].([]interface{})
assert.Equal(t, true, containsString(containerBinds, "/var/ecs/other/:/container/other/"), "Container Missing Optional Container Bind")
// validate mount point count
if containerMounts, ok := hostConfigMap["Mounts"].([]interface{}); ok {
assert.Equal(t, len(containerMounts), 3, "Task should have Required container binds (2) + 1 other bind")
} else {
t.Errorf("Unable to find 'Mounts' in container definition map")
}
// validate healthcheck
containerHealthCheck := configMap["Healthcheck"].(map[string]interface{})
containerHealthCheckTest := containerHealthCheck["Test"].([]interface{})
Expand Down Expand Up @@ -186,10 +190,80 @@ func TestFailCreateDaemonTask_MissingMount(t *testing.T) {
var hostConfigMap map[string]interface{}
json.Unmarshal([]byte(aws.StringValue(configRaw)), &configMap)
json.Unmarshal([]byte(aws.StringValue(hostConfigRaw)), &hostConfigMap)

// validate mount point count
containerBinds := hostConfigMap["Binds"].([]interface{})
assert.Equal(t, len(containerBinds), 3, "Task should have Required container binds (2) + 1 other bind")
if containerMounts, ok := hostConfigMap["Mounts"].([]interface{}); ok {
assert.Equal(t, len(containerMounts), 3, "Task should have Required container binds (2) + 1 other bind")
} else {
t.Errorf("Unable to find 'Mounts' in container definition map")
}
// validate healthcheck
containerHealthCheck := configMap["Healthcheck"].(map[string]interface{})
containerHealthCheckTest := containerHealthCheck["Test"].([]interface{})
assert.Equal(t, testHealthCheck[0], containerHealthCheckTest[0].(string), "Container health check has changed")
}

func TestCreateDaemonTask_PrivilegeAndMountPropagation(t *testing.T) {
// mock mkdirAllAndChown
origMkdir := mkdirAllAndChown
defer func() { mkdirAllAndChown = origMkdir }()
mkdirAllAndChown = func(path string, perm fs.FileMode, uid, gid int) error {
return nil
}
// set up test managed daemon
tmd := md.NewManagedDaemon(TestDaemonName, TestImageTag)
tmd.SetLoadedDaemonImageRef(TestImageRef)
tmd.SetPrivileged(true)
// test failure with a missing applicationLogMount
testAgentCommunicationMount := &md.MountPoint{SourceVolumeID: "agentCommunicationMount", ContainerPath: "/container/run/"}
testOtherMount := &md.MountPoint{SourceVolumeID: TestOtherVolumeID, ContainerPath: "/container/other/", SourceVolumeHostPath: "/var/ecs/other/", PropagationShared: true}
testMountPoints := []*md.MountPoint{}
testMountPoints = append(testMountPoints, testAgentCommunicationMount, testOtherMount)
tmd.SetMountPoints(testMountPoints)
testDaemonManager := NewDaemonManager(tmd)
_, err := testDaemonManager.CreateDaemonTask()
assert.EqualError(t, err, fmt.Sprintf("%s is an invalid managed daemon", TestDaemonName))

// add required log mount but no healthcheck
testApplicationLogMount := &md.MountPoint{SourceVolumeID: "applicationLogMount", ContainerPath: "/container/log/"}
testMountPoints = append(testMountPoints, testApplicationLogMount)
tmd.SetMountPoints(testMountPoints)
testDaemonManager = NewDaemonManager(tmd)
_, err = testDaemonManager.CreateDaemonTask()
assert.Nil(t, err)

// add required healthcheck
testHealthCheck := []string{"test"}
tmd.SetHealthCheck(testHealthCheck, 2*time.Minute, 2*time.Minute, 1)
testDaemonManager = NewDaemonManager(tmd)
resultDaemonTask, err := testDaemonManager.CreateDaemonTask()

// validate daemon task configs
assert.Equal(t, fmt.Sprintf("arn:::::/%s-", TestDaemonName), resultDaemonTask.Arn[:20], "Task Arn prefix should match Image Name ")
assert.Equal(t, apitaskstatus.TaskRunning, resultDaemonTask.DesiredStatusUnsafe, "Task DesiredStatus should be running")
assert.Equal(t, apitask.HostNetworkMode, resultDaemonTask.NetworkMode, "Task NetworkMode should be Host")
assert.Equal(t, "EC2", resultDaemonTask.LaunchType, "Task LaunchType should be EC2")
assert.Equal(t, true, resultDaemonTask.IsInternal, "Task IsInteral should be true")

// validate task container
assert.Equal(t, TestImageRef, resultDaemonTask.Containers[0].Image, "Task Container Image Name should match image ref")

// validate daemon container configs
configRaw := resultDaemonTask.Containers[0].DockerConfig.Config
hostConfigRaw := resultDaemonTask.Containers[0].DockerConfig.HostConfig
var configMap map[string]interface{}
var hostConfigMap map[string]interface{}
json.Unmarshal([]byte(aws.StringValue(configRaw)), &configMap)
json.Unmarshal([]byte(aws.StringValue(hostConfigRaw)), &hostConfigMap)
// validate mount point has One mount with Shared Propagation
if containerMounts, ok := hostConfigMap["Mounts"].([]interface{}); ok {
res := strings.Count(fmt.Sprintf("%v", containerMounts), "Propagation:shared")
assert.Equal(t, res, 1, "Task should have only one mount with shared Propagation")
} else {
t.Errorf("missing 'Mounts' in hostConfigMap")
}
// validate privileged
containerPrivileged := hostConfigMap["Privileged"].(bool)
assert.True(t, containerPrivileged, "Daemon Container should be privileged")
// validate healthcheck
containerHealthCheck := configMap["Healthcheck"].(map[string]interface{})
containerHealthCheckTest := containerHealthCheck["Test"].([]interface{})
Expand Down
Loading

0 comments on commit 106d15b

Please sign in to comment.