Skip to content

Commit 75db5cb

Browse files
authored
Load Managed Daemon images in background (#3984)
1 parent df18404 commit 75db5cb

File tree

16 files changed

+479
-74
lines changed

16 files changed

+479
-74
lines changed

agent/app/agent.go

+41-8
Original file line numberDiff line numberDiff line change
@@ -433,19 +433,15 @@ func (agent *ecsAgent) doStart(containerChangeEventStream *eventstream.EventStre
433433
return exitcodes.ExitTerminal
434434
}
435435

436+
// Load Managed Daemon images asynchronously
437+
agent.loadManagedDaemonImagesAsync(imageManager)
438+
436439
scManager := agent.serviceconnectManager
437440
scManager.SetECSClient(client, agent.containerInstanceARN)
438441
if loaded, _ := scManager.IsLoaded(agent.dockerClient); loaded {
439442
imageManager.AddImageToCleanUpExclusionList(agent.serviceconnectManager.GetLoadedImageName())
440443
}
441444

442-
// exclude all daemon images from cleanup
443-
for _, csiDM := range agent.daemonManagers {
444-
if loaded, _ := csiDM.IsLoaded(agent.dockerClient); loaded {
445-
imageManager.AddImageToCleanUpExclusionList(csiDM.GetManagedDaemon().GetLoadedDaemonImageRef())
446-
}
447-
}
448-
449445
// Add container instance ARN to metadata manager
450446
if agent.cfg.ContainerMetadataEnabled.Enabled() {
451447
agent.metadataManager.SetContainerInstanceARN(agent.containerInstanceARN)
@@ -485,7 +481,7 @@ func (agent *ecsAgent) doStart(containerChangeEventStream *eventstream.EventStre
485481
agent.startAsyncRoutines(containerChangeEventStream, credentialsManager, imageManager,
486482
taskEngine, deregisterInstanceEventStream, client, taskHandler, attachmentEventHandler, state, doctor)
487483
// TODO add EBS watcher to async routines
488-
agent.startEBSWatcher(state, taskEngine)
484+
agent.startEBSWatcher(state, taskEngine, agent.dockerClient)
489485
// Start the acs session, which should block doStart
490486
return agent.startACSSession(credentialsManager, taskEngine,
491487
deregisterInstanceEventStream, client, state, taskHandler, doctor)
@@ -751,6 +747,38 @@ func (agent *ecsAgent) constructVPCSubnetAttributes() []*ecs.Attribute {
751747
}
752748
}
753749

750+
// Loads Managed Daemon images for all Managed Daemons registered on the Agent.
751+
// The images are loaded in the background. Successfully loaded images are added to
752+
// imageManager's cleanup exclusion list.
753+
func (agent *ecsAgent) loadManagedDaemonImagesAsync(imageManager engine.ImageManager) {
754+
daemonManagers := agent.getDaemonManagers()
755+
logger.Debug(fmt.Sprintf("Will load images for %d Managed Daemons", len(daemonManagers)))
756+
for _, daemonManager := range daemonManagers {
757+
go agent.loadManagedDaemonImage(daemonManager, imageManager)
758+
}
759+
}
760+
761+
// Loads Managed Daemon image and adds it to image cleanup exclusion list upon success.
762+
func (agent *ecsAgent) loadManagedDaemonImage(dm dm.DaemonManager, imageManager engine.ImageManager) {
763+
imageRef := dm.GetManagedDaemon().GetImageRef()
764+
logger.Info("Starting to load Managed Daemon image", logger.Fields{
765+
field.ImageRef: imageRef,
766+
})
767+
image, err := dm.LoadImage(agent.ctx, agent.dockerClient)
768+
if err != nil {
769+
logger.Error("Failed to load Managed Daemon image", logger.Fields{
770+
field.ImageRef: imageRef,
771+
field.Error: err,
772+
})
773+
return
774+
}
775+
logger.Info("Successfully loaded Managed Daemon image", logger.Fields{
776+
field.ImageRef: imageRef,
777+
field.ImageID: image.ID,
778+
})
779+
imageManager.AddImageToCleanUpExclusionList(imageRef)
780+
}
781+
754782
// registerContainerInstance registers the container instance ID for the ECS Agent
755783
func (agent *ecsAgent) registerContainerInstance(
756784
client api.ECSClient,
@@ -1140,6 +1168,11 @@ func (agent *ecsAgent) setDaemonManager(key string, val dm.DaemonManager) {
11401168
agent.daemonManagers[key] = val
11411169
}
11421170

1171+
// Returns daemon managers map. Not designed to be thread-safe.
1172+
func (agent *ecsAgent) getDaemonManagers() map[string]dm.DaemonManager {
1173+
return agent.daemonManagers
1174+
}
1175+
11431176
// setVPCSubnet sets the vpc and subnet ids for the agent by querying the
11441177
// instance metadata service
11451178
func (agent *ecsAgent) setVPCSubnet() (error, bool) {

agent/app/agent_capability.go

+9-5
Original file line numberDiff line numberDiff line change
@@ -522,12 +522,16 @@ func (agent *ecsAgent) appendEBSTaskAttachCapabilities(capabilities []*ecs.Attri
522522
if daemonDef.GetImageName() == md.EbsCsiDriver {
523523
csiDaemonManager := dm.NewDaemonManager(daemonDef)
524524
agent.setDaemonManager(md.EbsCsiDriver, csiDaemonManager)
525-
if _, err := csiDaemonManager.LoadImage(agent.ctx, agent.dockerClient); err != nil {
526-
logger.Error("Failed to load the EBS CSI Driver. This container instance will not be able to support EBS Task Attach",
525+
imageExists, err := csiDaemonManager.ImageExists()
526+
if !imageExists {
527+
logger.Error(
528+
"Either EBS Daemon image does not exist or failed to check its existence."+
529+
" This container instance will not advertise EBS Task Attach capability.",
527530
logger.Fields{
528-
field.Error: err,
529-
},
530-
)
531+
field.ImageName: csiDaemonManager.GetManagedDaemon().GetImageName(),
532+
field.ImageTARPath: csiDaemonManager.GetManagedDaemon().GetImageTarPath(),
533+
field.Error: err,
534+
})
531535
return capabilities
532536
}
533537
}

agent/app/agent_test.go

+47
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ import (
5959
"github.com/aws/aws-sdk-go/aws"
6060
"github.com/aws/aws-sdk-go/aws/awserr"
6161
aws_credentials "github.com/aws/aws-sdk-go/aws/credentials"
62+
"github.com/docker/docker/api/types"
6263
"github.com/golang/mock/gomock"
6364
"github.com/stretchr/testify/assert"
6465
"github.com/stretchr/testify/require"
@@ -1859,3 +1860,49 @@ func TestWaitUntilInstanceInServicePolling(t *testing.T) {
18591860
})
18601861
}
18611862
}
1863+
1864+
func TestLoadManagedDaemonImage(t *testing.T) {
1865+
tcs := []struct {
1866+
name string
1867+
setDaemonManagerExpectations func(*mock_daemonmanager.MockDaemonManager)
1868+
setImageManagerExpectations func(*mock_engine.MockImageManager)
1869+
}{
1870+
{
1871+
name: "no exclusion list update if image load fails",
1872+
setDaemonManagerExpectations: func(mdm *mock_daemonmanager.MockDaemonManager) {
1873+
mdm.EXPECT().GetManagedDaemon().Return(md.NewManagedDaemon("name", "tag")).Times(2)
1874+
mdm.EXPECT().LoadImage(gomock.Any(), gomock.Any()).Return(nil, errors.New("error"))
1875+
},
1876+
},
1877+
{
1878+
name: "exclusion list is updated if image load succeeds",
1879+
setDaemonManagerExpectations: func(mdm *mock_daemonmanager.MockDaemonManager) {
1880+
mdm.EXPECT().GetManagedDaemon().Return(md.NewManagedDaemon("name", "tag")).Times(3)
1881+
mdm.EXPECT().
1882+
LoadImage(gomock.Any(), gomock.Any()).
1883+
Return(&types.ImageInspect{ID: "image-id"}, nil)
1884+
},
1885+
setImageManagerExpectations: func(mim *mock_engine.MockImageManager) {
1886+
mim.EXPECT().AddImageToCleanUpExclusionList("name:tag")
1887+
},
1888+
},
1889+
}
1890+
for _, tc := range tcs {
1891+
t.Run(tc.name, func(t *testing.T) {
1892+
ctrl := gomock.NewController(t)
1893+
ctrl.Finish()
1894+
1895+
dockerClient := mock_dockerapi.NewMockDockerClient(ctrl)
1896+
daemonManager := mock_daemonmanager.NewMockDaemonManager(ctrl)
1897+
imageManager := mock_engine.NewMockImageManager(ctrl)
1898+
1899+
tc.setDaemonManagerExpectations(daemonManager)
1900+
if tc.setImageManagerExpectations != nil {
1901+
tc.setImageManagerExpectations(imageManager)
1902+
}
1903+
1904+
agent := &ecsAgent{ctx: context.Background(), dockerClient: dockerClient}
1905+
agent.loadManagedDaemonImage(daemonManager, imageManager)
1906+
})
1907+
}
1908+
}

agent/app/agent_unix.go

+7-2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121

2222
asmfactory "github.com/aws/amazon-ecs-agent/agent/asm/factory"
2323
"github.com/aws/amazon-ecs-agent/agent/config"
24+
"github.com/aws/amazon-ecs-agent/agent/dockerclient/dockerapi"
2425
ebs "github.com/aws/amazon-ecs-agent/agent/ebs"
2526
"github.com/aws/amazon-ecs-agent/agent/ecscni"
2627
"github.com/aws/amazon-ecs-agent/agent/engine"
@@ -151,10 +152,14 @@ func (agent *ecsAgent) startENIWatcher(state dockerstate.TaskEngineState, stateC
151152
return nil
152153
}
153154

154-
func (agent *ecsAgent) startEBSWatcher(state dockerstate.TaskEngineState, taskEngine engine.TaskEngine) {
155+
func (agent *ecsAgent) startEBSWatcher(
156+
state dockerstate.TaskEngineState,
157+
taskEngine engine.TaskEngine,
158+
dockerClient dockerapi.DockerClient,
159+
) {
155160
if agent.ebsWatcher == nil {
156161
seelog.Debug("Creating new EBS watcher...")
157-
agent.ebsWatcher = ebs.NewWatcher(agent.ctx, state, taskEngine)
162+
agent.ebsWatcher = ebs.NewWatcher(agent.ctx, state, taskEngine, dockerClient)
158163
go agent.ebsWatcher.Start()
159164
}
160165
}

agent/app/agent_windows.go

+6-2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424

2525
asmfactory "github.com/aws/amazon-ecs-agent/agent/asm/factory"
2626
"github.com/aws/amazon-ecs-agent/agent/data"
27+
"github.com/aws/amazon-ecs-agent/agent/dockerclient/dockerapi"
2728
"github.com/aws/amazon-ecs-agent/agent/ecscni"
2829
"github.com/aws/amazon-ecs-agent/agent/engine"
2930
"github.com/aws/amazon-ecs-agent/agent/engine/dockerstate"
@@ -99,9 +100,12 @@ func (agent *ecsAgent) startWindowsService() int {
99100
return 0
100101
}
101102

102-
func (agent *ecsAgent) startEBSWatcher(state dockerstate.TaskEngineState, taskEngine engine.TaskEngine) error {
103+
func (agent *ecsAgent) startEBSWatcher(
104+
state dockerstate.TaskEngineState,
105+
taskEngine engine.TaskEngine,
106+
dockerClient dockerapi.DockerClient,
107+
) {
103108
seelog.Debug("Windows EBS Watcher not implemented: No Op")
104-
return nil
105109
}
106110

107111
// handler implements https://godoc.org/golang.org/x/sys/windows/svc#Handler

0 commit comments

Comments
 (0)