diff --git a/agent/app/agent.go b/agent/app/agent.go index eeea01ec852..e0797da16be 100644 --- a/agent/app/agent.go +++ b/agent/app/agent.go @@ -260,6 +260,11 @@ func (agent *ecsAgent) doStart(containerChangeEventStream *eventstream.EventStre return exitcodes.ExitTerminal } + err = agent.loadPauseContainer() + if err != nil { + seelog.Error("Failed to load pause container: %v", err) + } + var vpcSubnetAttributes []*ecs.Attribute // Check if Task ENI is enabled if agent.cfg.TaskENIEnabled { diff --git a/agent/app/agent_capability_test.go b/agent/app/agent_capability_test.go index 4e553a7d867..1e673fcf968 100644 --- a/agent/app/agent_capability_test.go +++ b/agent/app/agent_capability_test.go @@ -17,6 +17,7 @@ package app import ( "errors" + mock_pause "github.com/aws/amazon-ecs-agent/agent/eni/pause/mocks" "strings" "testing" @@ -46,6 +47,7 @@ func TestCapabilities(t *testing.T) { cniClient := mock_ecscni.NewMockCNIClient(ctrl) mockCredentialsProvider := app_mocks.NewMockProvider(ctrl) mockMobyPlugins := mock_mobypkgwrapper.NewMockPlugins(ctrl) + mockPauseLoader := mock_pause.NewMockLoader(ctrl) conf := &config.Config{ AvailableLoggingDrivers: []dockerclient.LoggingDriver{ dockerclient.JSONFileDriver, @@ -61,6 +63,8 @@ func TestCapabilities(t *testing.T) { AWSVPCBlockInstanceMetdata: true, TaskCleanupWaitDuration: config.DefaultConfig().TaskCleanupWaitDuration, } + + mockPauseLoader.EXPECT().IsLoaded(gomock.Any()).Return(false, nil).AnyTimes() // Scan() and ListPluginsWithFilters() are tested with // AnyTimes() because they are not called in windows. gomock.InOrder( @@ -142,6 +146,7 @@ func TestCapabilities(t *testing.T) { cfg: conf, dockerClient: client, cniClient: cniClient, + pauseLoader: mockPauseLoader, credentialProvider: aws_credentials.NewCredentials(mockCredentialsProvider), mobyPlugins: mockMobyPlugins, } @@ -169,12 +174,16 @@ func TestCapabilitiesECR(t *testing.T) { client.EXPECT().ListPluginsWithFilters(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return([]string{}, nil) + mockPauseLoader := mock_pause.NewMockLoader(ctrl) + mockPauseLoader.EXPECT().IsLoaded(gomock.Any()).Return(false, nil).AnyTimes() + ctx, cancel := context.WithCancel(context.TODO()) // Cancel the context to cancel async routines defer cancel() agent := &ecsAgent{ ctx: ctx, cfg: conf, + pauseLoader: mockPauseLoader, dockerClient: client, mobyPlugins: mockMobyPlugins, } @@ -211,6 +220,9 @@ func TestCapabilitiesTaskIAMRoleForSupportedDockerVersion(t *testing.T) { client.EXPECT().ListPluginsWithFilters(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return([]string{}, nil) + mockPauseLoader := mock_pause.NewMockLoader(ctrl) + mockPauseLoader.EXPECT().IsLoaded(gomock.Any()).Return(false, nil).AnyTimes() + ctx, cancel := context.WithCancel(context.TODO()) // Cancel the context to cancel async routines defer cancel() @@ -218,6 +230,7 @@ func TestCapabilitiesTaskIAMRoleForSupportedDockerVersion(t *testing.T) { ctx: ctx, cfg: conf, dockerClient: client, + pauseLoader: mockPauseLoader, mobyPlugins: mockMobyPlugins, } capabilities, err := agent.capabilities() @@ -250,6 +263,9 @@ func TestCapabilitiesTaskIAMRoleForUnSupportedDockerVersion(t *testing.T) { client.EXPECT().ListPluginsWithFilters(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return([]string{}, nil) + mockPauseLoader := mock_pause.NewMockLoader(ctrl) + mockPauseLoader.EXPECT().IsLoaded(gomock.Any()).Return(false, nil).AnyTimes() + ctx, cancel := context.WithCancel(context.TODO()) // Cancel the context to cancel async routines defer cancel() @@ -257,6 +273,7 @@ func TestCapabilitiesTaskIAMRoleForUnSupportedDockerVersion(t *testing.T) { ctx: ctx, cfg: conf, dockerClient: client, + pauseLoader: mockPauseLoader, mobyPlugins: mockMobyPlugins, } @@ -290,6 +307,9 @@ func TestCapabilitiesTaskIAMRoleNetworkHostForSupportedDockerVersion(t *testing. client.EXPECT().ListPluginsWithFilters(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return([]string{}, nil) + mockPauseLoader := mock_pause.NewMockLoader(ctrl) + mockPauseLoader.EXPECT().IsLoaded(gomock.Any()).Return(false, nil).AnyTimes() + ctx, cancel := context.WithCancel(context.TODO()) // Cancel the context to cancel async routines defer cancel() @@ -297,6 +317,7 @@ func TestCapabilitiesTaskIAMRoleNetworkHostForSupportedDockerVersion(t *testing. ctx: ctx, cfg: conf, dockerClient: client, + pauseLoader: mockPauseLoader, mobyPlugins: mockMobyPlugins, } @@ -330,6 +351,9 @@ func TestCapabilitiesTaskIAMRoleNetworkHostForUnSupportedDockerVersion(t *testin client.EXPECT().ListPluginsWithFilters(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return([]string{}, nil) + mockPauseLoader := mock_pause.NewMockLoader(ctrl) + mockPauseLoader.EXPECT().IsLoaded(gomock.Any()).Return(false, nil).AnyTimes() + ctx, cancel := context.WithCancel(context.TODO()) // Cancel the context to cancel async routines defer cancel() @@ -337,6 +361,7 @@ func TestCapabilitiesTaskIAMRoleNetworkHostForUnSupportedDockerVersion(t *testin ctx: ctx, cfg: conf, dockerClient: client, + pauseLoader: mockPauseLoader, mobyPlugins: mockMobyPlugins, } @@ -359,6 +384,7 @@ func TestAWSVPCBlockInstanceMetadataWhenTaskENIIsDisabled(t *testing.T) { client := mock_dockerapi.NewMockDockerClient(ctrl) cniClient := mock_ecscni.NewMockCNIClient(ctrl) mockCredentialsProvider := app_mocks.NewMockProvider(ctrl) + mockPauseLoader := mock_pause.NewMockLoader(ctrl) conf := &config.Config{ AvailableLoggingDrivers: []dockerclient.LoggingDriver{ dockerclient.JSONFileDriver, @@ -368,6 +394,7 @@ func TestAWSVPCBlockInstanceMetadataWhenTaskENIIsDisabled(t *testing.T) { } mockMobyPlugins := mock_mobypkgwrapper.NewMockPlugins(ctrl) + mockPauseLoader.EXPECT().IsLoaded(gomock.Any()).Return(false, nil).AnyTimes() gomock.InOrder( client.EXPECT().SupportedVersions().Return([]dockerclient.DockerVersion{ dockerclient.Version_1_17, @@ -404,6 +431,7 @@ func TestAWSVPCBlockInstanceMetadataWhenTaskENIIsDisabled(t *testing.T) { cfg: conf, dockerClient: client, cniClient: cniClient, + pauseLoader: mockPauseLoader, credentialProvider: aws_credentials.NewCredentials(mockCredentialsProvider), mobyPlugins: mockMobyPlugins, } @@ -443,6 +471,9 @@ func TestCapabilitiesExecutionRoleAWSLogs(t *testing.T) { client.EXPECT().ListPluginsWithFilters(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return([]string{}, nil) + mockPauseLoader := mock_pause.NewMockLoader(ctrl) + mockPauseLoader.EXPECT().IsLoaded(gomock.Any()).Return(false, nil).AnyTimes() + ctx, cancel := context.WithCancel(context.TODO()) // Cancel the context to cancel async routines defer cancel() @@ -451,6 +482,7 @@ func TestCapabilitiesExecutionRoleAWSLogs(t *testing.T) { cfg: conf, dockerClient: client, cniClient: cniClient, + pauseLoader: mockPauseLoader, mobyPlugins: mockMobyPlugins, } @@ -474,6 +506,8 @@ func TestCapabilitiesTaskResourceLimit(t *testing.T) { client := mock_dockerapi.NewMockDockerClient(ctrl) versionList := []dockerclient.DockerVersion{dockerclient.Version_1_22} mockMobyPlugins := mock_mobypkgwrapper.NewMockPlugins(ctrl) + mockPauseLoader := mock_pause.NewMockLoader(ctrl) + mockPauseLoader.EXPECT().IsLoaded(gomock.Any()).Return(false, nil).AnyTimes() gomock.InOrder( client.EXPECT().SupportedVersions().Return(versionList), client.EXPECT().KnownVersions().Return(versionList), @@ -488,6 +522,7 @@ func TestCapabilitiesTaskResourceLimit(t *testing.T) { ctx: ctx, cfg: conf, dockerClient: client, + pauseLoader: mockPauseLoader, mobyPlugins: mockMobyPlugins, } @@ -514,6 +549,8 @@ func TestCapabilitesTaskResourceLimitDisabledByMissingDockerVersion(t *testing.T client := mock_dockerapi.NewMockDockerClient(ctrl) versionList := []dockerclient.DockerVersion{dockerclient.Version_1_19} mockMobyPlugins := mock_mobypkgwrapper.NewMockPlugins(ctrl) + mockPauseLoader := mock_pause.NewMockLoader(ctrl) + mockPauseLoader.EXPECT().IsLoaded(gomock.Any()).Return(false, nil).AnyTimes() gomock.InOrder( client.EXPECT().SupportedVersions().Return(versionList), client.EXPECT().KnownVersions().Return(versionList), @@ -528,6 +565,7 @@ func TestCapabilitesTaskResourceLimitDisabledByMissingDockerVersion(t *testing.T ctx: ctx, cfg: conf, dockerClient: client, + pauseLoader: mockPauseLoader, mobyPlugins: mockMobyPlugins, } @@ -553,6 +591,8 @@ func TestCapabilitesTaskResourceLimitErrorCase(t *testing.T) { client := mock_dockerapi.NewMockDockerClient(ctrl) versionList := []dockerclient.DockerVersion{dockerclient.Version_1_19} + mockPauseLoader := mock_pause.NewMockLoader(ctrl) + mockPauseLoader.EXPECT().IsLoaded(gomock.Any()).Return(false, nil).AnyTimes() gomock.InOrder( client.EXPECT().SupportedVersions().Return(versionList), client.EXPECT().KnownVersions().Return(versionList), @@ -563,6 +603,7 @@ func TestCapabilitesTaskResourceLimitErrorCase(t *testing.T) { agent := &ecsAgent{ ctx: ctx, cfg: conf, + pauseLoader: mockPauseLoader, dockerClient: client, } @@ -586,6 +627,9 @@ func TestCapabilitiesContainerHealth(t *testing.T) { client.EXPECT().ListPluginsWithFilters(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return([]string{}, nil) + mockPauseLoader := mock_pause.NewMockLoader(ctrl) + mockPauseLoader.EXPECT().IsLoaded(gomock.Any()).Return(false, nil) + ctx, cancel := context.WithCancel(context.TODO()) // Cancel the context to cancel async routines defer cancel() @@ -593,6 +637,7 @@ func TestCapabilitiesContainerHealth(t *testing.T) { ctx: ctx, cfg: &config.Config{}, dockerClient: client, + pauseLoader: mockPauseLoader, mobyPlugins: mockMobyPlugins, } @@ -623,6 +668,9 @@ func TestCapabilitiesContainerHealthDisabled(t *testing.T) { client.EXPECT().ListPluginsWithFilters(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return([]string{}, nil) + mockPauseLoader := mock_pause.NewMockLoader(ctrl) + mockPauseLoader.EXPECT().IsLoaded(gomock.Any()).Return(false, nil) + ctx, cancel := context.WithCancel(context.TODO()) // Cancel the context to cancel async routines defer cancel() @@ -630,6 +678,7 @@ func TestCapabilitiesContainerHealthDisabled(t *testing.T) { ctx: ctx, cfg: &config.Config{DisableDockerHealthCheck: true}, dockerClient: client, + pauseLoader: mockPauseLoader, mobyPlugins: mockMobyPlugins, } @@ -651,6 +700,8 @@ func TestCapabilitesListPluginsErrorCase(t *testing.T) { client := mock_dockerapi.NewMockDockerClient(ctrl) versionList := []dockerclient.DockerVersion{dockerclient.Version_1_19} + mockPauseLoader := mock_pause.NewMockLoader(ctrl) + mockPauseLoader.EXPECT().IsLoaded(gomock.Any()).Return(false, nil) gomock.InOrder( client.EXPECT().SupportedVersions().Return(versionList), client.EXPECT().KnownVersions().Return(versionList), @@ -665,6 +716,7 @@ func TestCapabilitesListPluginsErrorCase(t *testing.T) { ctx: ctx, cfg: &config.Config{}, dockerClient: client, + pauseLoader: mockPauseLoader, mobyPlugins: mockMobyPlugins, } @@ -685,6 +737,8 @@ func TestCapabilitesScanPluginsErrorCase(t *testing.T) { client := mock_dockerapi.NewMockDockerClient(ctrl) versionList := []dockerclient.DockerVersion{dockerclient.Version_1_19} + mockPauseLoader := mock_pause.NewMockLoader(ctrl) + mockPauseLoader.EXPECT().IsLoaded(gomock.Any()).Return(false, nil) gomock.InOrder( client.EXPECT().SupportedVersions().Return(versionList), client.EXPECT().KnownVersions().Return(versionList), @@ -699,6 +753,7 @@ func TestCapabilitesScanPluginsErrorCase(t *testing.T) { ctx: ctx, cfg: &config.Config{}, dockerClient: client, + pauseLoader: mockPauseLoader, mobyPlugins: mockMobyPlugins, } diff --git a/agent/app/agent_capability_unix.go b/agent/app/agent_capability_unix.go index b86f7e08793..fd70f583432 100644 --- a/agent/app/agent_capability_unix.go +++ b/agent/app/agent_capability_unix.go @@ -111,6 +111,11 @@ func (agent *ecsAgent) appendBranchENIPluginVersionAttribute(capabilities []*ecs } func (agent *ecsAgent) appendPIDAndIPCNamespaceSharingCapabilities(capabilities []*ecs.Attribute) []*ecs.Attribute { + isLoaded, err := agent.pauseLoader.IsLoaded(agent.dockerClient) + if !isLoaded || err != nil { + seelog.Warnf("Pause container is not loaded, did not append PID and IPC capabilities: %v", err) + return capabilities + } return appendNameOnlyAttribute(capabilities, attributePrefix+capabiltyPIDAndIPCNamespaceSharing) } diff --git a/agent/app/agent_capability_unix_test.go b/agent/app/agent_capability_unix_test.go index af9429b815c..48030916aa5 100644 --- a/agent/app/agent_capability_unix_test.go +++ b/agent/app/agent_capability_unix_test.go @@ -17,6 +17,8 @@ package app import ( "context" + "errors" + mock_pause "github.com/aws/amazon-ecs-agent/agent/eni/pause/mocks" "os" "path/filepath" "testing" @@ -46,6 +48,7 @@ func TestVolumeDriverCapabilitiesUnix(t *testing.T) { cniClient := mock_ecscni.NewMockCNIClient(ctrl) mockCredentialsProvider := app_mocks.NewMockProvider(ctrl) mockMobyPlugins := mock_mobypkgwrapper.NewMockPlugins(ctrl) + mockPauseLoader := mock_pause.NewMockLoader(ctrl) conf := &config.Config{ AvailableLoggingDrivers: []dockerclient.LoggingDriver{ dockerclient.JSONFileDriver, @@ -62,6 +65,7 @@ func TestVolumeDriverCapabilitiesUnix(t *testing.T) { TaskCleanupWaitDuration: config.DefaultConfig().TaskCleanupWaitDuration, } + mockPauseLoader.EXPECT().IsLoaded(gomock.Any()).Return(true, nil) gomock.InOrder( client.EXPECT().SupportedVersions().Return([]dockerclient.DockerVersion{ dockerclient.Version_1_17, @@ -130,6 +134,7 @@ func TestVolumeDriverCapabilitiesUnix(t *testing.T) { cfg: conf, dockerClient: client, cniClient: cniClient, + pauseLoader: mockPauseLoader, credentialProvider: aws_credentials.NewCredentials(mockCredentialsProvider), mobyPlugins: mockMobyPlugins, } @@ -149,11 +154,13 @@ func TestNvidiaDriverCapabilitiesUnix(t *testing.T) { client := mock_dockerapi.NewMockDockerClient(ctrl) mockMobyPlugins := mock_mobypkgwrapper.NewMockPlugins(ctrl) mockCredentialsProvider := app_mocks.NewMockProvider(ctrl) + mockPauseLoader := mock_pause.NewMockLoader(ctrl) conf := &config.Config{ PrivilegedDisabled: true, GPUSupportEnabled: true, } + mockPauseLoader.EXPECT().IsLoaded(gomock.Any()).Return(true, nil) gomock.InOrder( client.EXPECT().SupportedVersions().Return([]dockerclient.DockerVersion{ dockerclient.Version_1_17, @@ -203,6 +210,7 @@ func TestNvidiaDriverCapabilitiesUnix(t *testing.T) { ctx: ctx, cfg: conf, dockerClient: client, + pauseLoader: mockPauseLoader, credentialProvider: aws_credentials.NewCredentials(mockCredentialsProvider), mobyPlugins: mockMobyPlugins, resourceFields: &taskresource.ResourceFields{ @@ -227,11 +235,13 @@ func TestEmptyNvidiaDriverCapabilitiesUnix(t *testing.T) { client := mock_dockerapi.NewMockDockerClient(ctrl) mockMobyPlugins := mock_mobypkgwrapper.NewMockPlugins(ctrl) mockCredentialsProvider := app_mocks.NewMockProvider(ctrl) + mockPauseLoader := mock_pause.NewMockLoader(ctrl) conf := &config.Config{ PrivilegedDisabled: true, GPUSupportEnabled: true, } + mockPauseLoader.EXPECT().IsLoaded(gomock.Any()).Return(true, nil) gomock.InOrder( client.EXPECT().SupportedVersions().Return([]dockerclient.DockerVersion{ dockerclient.Version_1_17, @@ -278,6 +288,7 @@ func TestEmptyNvidiaDriverCapabilitiesUnix(t *testing.T) { ctx: ctx, cfg: conf, dockerClient: client, + pauseLoader: mockPauseLoader, credentialProvider: aws_credentials.NewCredentials(mockCredentialsProvider), mobyPlugins: mockMobyPlugins, resourceFields: &taskresource.ResourceFields{ @@ -303,12 +314,14 @@ func TestENITrunkingCapabilitiesUnix(t *testing.T) { cniClient := mock_ecscni.NewMockCNIClient(ctrl) mockMobyPlugins := mock_mobypkgwrapper.NewMockPlugins(ctrl) mockCredentialsProvider := app_mocks.NewMockProvider(ctrl) + mockPauseLoader := mock_pause.NewMockLoader(ctrl) conf := &config.Config{ PrivilegedDisabled: true, TaskENIEnabled: true, ENITrunkingEnabled: true, } + mockPauseLoader.EXPECT().IsLoaded(gomock.Any()).Return(true, nil) gomock.InOrder( client.EXPECT().SupportedVersions().Return([]dockerclient.DockerVersion{ dockerclient.Version_1_17, @@ -369,6 +382,7 @@ func TestENITrunkingCapabilitiesUnix(t *testing.T) { cfg: conf, dockerClient: client, cniClient: cniClient, + pauseLoader: mockPauseLoader, credentialProvider: aws_credentials.NewCredentials(mockCredentialsProvider), mobyPlugins: mockMobyPlugins, } @@ -389,12 +403,14 @@ func TestNoENITrunkingCapabilitiesUnix(t *testing.T) { cniClient := mock_ecscni.NewMockCNIClient(ctrl) mockMobyPlugins := mock_mobypkgwrapper.NewMockPlugins(ctrl) mockCredentialsProvider := app_mocks.NewMockProvider(ctrl) + mockPauseLoader := mock_pause.NewMockLoader(ctrl) conf := &config.Config{ PrivilegedDisabled: true, TaskENIEnabled: true, ENITrunkingEnabled: false, } + mockPauseLoader.EXPECT().IsLoaded(gomock.Any()).Return(true, nil) gomock.InOrder( client.EXPECT().SupportedVersions().Return([]dockerclient.DockerVersion{ dockerclient.Version_1_17, @@ -447,6 +463,7 @@ func TestNoENITrunkingCapabilitiesUnix(t *testing.T) { cfg: conf, dockerClient: client, cniClient: cniClient, + pauseLoader: mockPauseLoader, credentialProvider: aws_credentials.NewCredentials(mockCredentialsProvider), mobyPlugins: mockMobyPlugins, } @@ -466,10 +483,12 @@ func TestPIDAndIPCNamespaceSharingCapabilitiesUnix(t *testing.T) { client := mock_dockerapi.NewMockDockerClient(ctrl) mockMobyPlugins := mock_mobypkgwrapper.NewMockPlugins(ctrl) mockCredentialsProvider := app_mocks.NewMockProvider(ctrl) + mockPauseLoader := mock_pause.NewMockLoader(ctrl) conf := &config.Config{ PrivilegedDisabled: true, } + mockPauseLoader.EXPECT().IsLoaded(gomock.Any()).Return(true, nil) gomock.InOrder( client.EXPECT().SupportedVersions().Return([]dockerclient.DockerVersion{ dockerclient.Version_1_17, @@ -532,6 +551,92 @@ func TestPIDAndIPCNamespaceSharingCapabilitiesUnix(t *testing.T) { ctx: ctx, cfg: conf, dockerClient: client, + pauseLoader: mockPauseLoader, + credentialProvider: aws_credentials.NewCredentials(mockCredentialsProvider), + mobyPlugins: mockMobyPlugins, + } + capabilities, err := agent.capabilities() + assert.NoError(t, err) + + for i, expected := range expectedCapabilities { + assert.Equal(t, aws.StringValue(expected.Name), aws.StringValue(capabilities[i].Name)) + assert.Equal(t, aws.StringValue(expected.Value), aws.StringValue(capabilities[i].Value)) + } +} + +func TestPIDAndIPCNamespaceSharingCapabilitiesNoPauseContainer(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + client := mock_dockerapi.NewMockDockerClient(ctrl) + mockMobyPlugins := mock_mobypkgwrapper.NewMockPlugins(ctrl) + mockCredentialsProvider := app_mocks.NewMockProvider(ctrl) + mockPauseLoader := mock_pause.NewMockLoader(ctrl) + conf := &config.Config{ + PrivilegedDisabled: true, + } + + mockPauseLoader.EXPECT().IsLoaded(gomock.Any()).Return(false, errors.New("mock error")) + gomock.InOrder( + client.EXPECT().SupportedVersions().Return([]dockerclient.DockerVersion{ + dockerclient.Version_1_17, + }), + client.EXPECT().KnownVersions().Return([]dockerclient.DockerVersion{ + dockerclient.Version_1_17, + }), + mockMobyPlugins.EXPECT().Scan().AnyTimes().Return([]string{}, nil), + client.EXPECT().ListPluginsWithFilters(gomock.Any(), gomock.Any(), gomock.Any(), + gomock.Any()).AnyTimes().Return([]string{}, nil), + ) + + expectedCapabilityNames := []string{ + "com.amazonaws.ecs.capability.docker-remote-api.1.17", + } + + var expectedCapabilities []*ecs.Attribute + for _, name := range expectedCapabilityNames { + expectedCapabilities = append(expectedCapabilities, + &ecs.Attribute{Name: aws.String(name)}) + } + expectedCapabilities = append(expectedCapabilities, + []*ecs.Attribute{ + // linux specific capabilities + { + Name: aws.String("ecs.capability.docker-plugin.local"), + }, + { + Name: aws.String(attributePrefix + capabilityPrivateRegistryAuthASM), + }, + { + Name: aws.String(attributePrefix + capabilitySecretEnvSSM), + }, + { + Name: aws.String(attributePrefix + capabilitySecretLogDriverSSM), + }, + { + Name: aws.String(attributePrefix + capabilityECREndpoint), + }, + { + Name: aws.String(attributePrefix + capabilitySecretEnvASM), + }, + { + Name: aws.String(attributePrefix + capabilitySecretLogDriverASM), + }, + { + Name: aws.String(attributePrefix + capabilityContainerOrdering), + }, + { + Name: aws.String(attributePrefix + capabilityFullTaskSync), + }, + }...) + ctx, cancel := context.WithCancel(context.TODO()) + // Cancel the context to cancel async routines + defer cancel() + agent := &ecsAgent{ + ctx: ctx, + cfg: conf, + dockerClient: client, + pauseLoader: mockPauseLoader, credentialProvider: aws_credentials.NewCredentials(mockCredentialsProvider), mobyPlugins: mockMobyPlugins, } @@ -551,10 +656,12 @@ func TestAppMeshCapabilitiesUnix(t *testing.T) { client := mock_dockerapi.NewMockDockerClient(ctrl) mockMobyPlugins := mock_mobypkgwrapper.NewMockPlugins(ctrl) mockCredentialsProvider := app_mocks.NewMockProvider(ctrl) + mockPauseLoader := mock_pause.NewMockLoader(ctrl) conf := &config.Config{ PrivilegedDisabled: true, } + mockPauseLoader.EXPECT().IsLoaded(gomock.Any()).Return(true, nil) gomock.InOrder( client.EXPECT().SupportedVersions().Return([]dockerclient.DockerVersion{ dockerclient.Version_1_17, @@ -620,6 +727,7 @@ func TestAppMeshCapabilitiesUnix(t *testing.T) { ctx: ctx, cfg: conf, dockerClient: client, + pauseLoader: mockPauseLoader, credentialProvider: aws_credentials.NewCredentials(mockCredentialsProvider), mobyPlugins: mockMobyPlugins, } @@ -644,10 +752,12 @@ func TestTaskEIACapabilitiesNoOptimizedCPU(t *testing.T) { client := mock_dockerapi.NewMockDockerClient(ctrl) mockMobyPlugins := mock_mobypkgwrapper.NewMockPlugins(ctrl) mockCredentialsProvider := app_mocks.NewMockProvider(ctrl) + mockPauseLoader := mock_pause.NewMockLoader(ctrl) conf := &config.Config{ PrivilegedDisabled: true, } + mockPauseLoader.EXPECT().IsLoaded(gomock.Any()).Return(true, nil) gomock.InOrder( client.EXPECT().SupportedVersions().Return([]dockerclient.DockerVersion{ dockerclient.Version_1_17, @@ -667,6 +777,7 @@ func TestTaskEIACapabilitiesNoOptimizedCPU(t *testing.T) { ctx: ctx, cfg: conf, dockerClient: client, + pauseLoader: mockPauseLoader, credentialProvider: aws_credentials.NewCredentials(mockCredentialsProvider), mobyPlugins: mockMobyPlugins, } @@ -683,6 +794,7 @@ func TestTaskEIACapabilitiesWithOptimizedCPU(t *testing.T) { client := mock_dockerapi.NewMockDockerClient(ctrl) mockMobyPlugins := mock_mobypkgwrapper.NewMockPlugins(ctrl) mockCredentialsProvider := app_mocks.NewMockProvider(ctrl) + mockPauseLoader := mock_pause.NewMockLoader(ctrl) conf := &config.Config{ PrivilegedDisabled: true, @@ -693,6 +805,7 @@ func TestTaskEIACapabilitiesWithOptimizedCPU(t *testing.T) { } defer resetOpenFile() + mockPauseLoader.EXPECT().IsLoaded(gomock.Any()).Return(true, nil) gomock.InOrder( client.EXPECT().SupportedVersions().Return([]dockerclient.DockerVersion{ dockerclient.Version_1_17, @@ -712,6 +825,7 @@ func TestTaskEIACapabilitiesWithOptimizedCPU(t *testing.T) { ctx: ctx, cfg: conf, dockerClient: client, + pauseLoader: mockPauseLoader, credentialProvider: aws_credentials.NewCredentials(mockCredentialsProvider), mobyPlugins: mockMobyPlugins, } @@ -730,10 +844,12 @@ func TestAWSLoggingDriverAndLogRouterCapabilitiesUnix(t *testing.T) { client := mock_dockerapi.NewMockDockerClient(ctrl) mockMobyPlugins := mock_mobypkgwrapper.NewMockPlugins(ctrl) mockCredentialsProvider := app_mocks.NewMockProvider(ctrl) + mockPauseLoader := mock_pause.NewMockLoader(ctrl) conf := &config.Config{ PrivilegedDisabled: true, } + mockPauseLoader.EXPECT().IsLoaded(gomock.Any()).Return(true, nil) gomock.InOrder( client.EXPECT().SupportedVersions().Return([]dockerclient.DockerVersion{ dockerclient.Version_1_17, @@ -808,6 +924,7 @@ func TestAWSLoggingDriverAndLogRouterCapabilitiesUnix(t *testing.T) { ctx: ctx, cfg: conf, dockerClient: client, + pauseLoader: mockPauseLoader, credentialProvider: aws_credentials.NewCredentials(mockCredentialsProvider), mobyPlugins: mockMobyPlugins, } @@ -828,10 +945,12 @@ func TestFirelensConfigCapabilitiesUnix(t *testing.T) { client := mock_dockerapi.NewMockDockerClient(ctrl) mockMobyPlugins := mock_mobypkgwrapper.NewMockPlugins(ctrl) mockCredentialsProvider := app_mocks.NewMockProvider(ctrl) + mockPauseLoader := mock_pause.NewMockLoader(ctrl) conf := &config.Config{ PrivilegedDisabled: true, } + mockPauseLoader.EXPECT().IsLoaded(gomock.Any()).Return(true, nil) gomock.InOrder( client.EXPECT().SupportedVersions().Return([]dockerclient.DockerVersion{ dockerclient.Version_1_17, @@ -851,6 +970,7 @@ func TestFirelensConfigCapabilitiesUnix(t *testing.T) { ctx: ctx, cfg: conf, dockerClient: client, + pauseLoader: mockPauseLoader, credentialProvider: aws_credentials.NewCredentials(mockCredentialsProvider), mobyPlugins: mockMobyPlugins, } diff --git a/agent/app/agent_test.go b/agent/app/agent_test.go index f2abcbdc14a..96401a39dcb 100644 --- a/agent/app/agent_test.go +++ b/agent/app/agent_test.go @@ -19,6 +19,7 @@ import ( "context" "errors" "fmt" + mock_pause "github.com/aws/amazon-ecs-agent/agent/eni/pause/mocks" "sort" "sync" "testing" @@ -238,7 +239,10 @@ func TestDoStartRegisterContainerInstanceErrorTerminal(t *testing.T) { mockCredentialsProvider := app_mocks.NewMockProvider(ctrl) mockMobyPlugins := mock_mobypkgwrapper.NewMockPlugins(ctrl) mockEC2Metadata := mock_ec2.NewMockEC2MetadataClient(ctrl) + mockPauseLoader := mock_pause.NewMockLoader(ctrl) + mockPauseLoader.EXPECT().IsLoaded(gomock.Any()).Return(false, nil).AnyTimes() + mockPauseLoader.EXPECT().LoadImage(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() gomock.InOrder( dockerClient.EXPECT().SupportedVersions().Return(apiVersions), mockCredentialsProvider.EXPECT().Retrieve().Return(aws_credentials.Value{}, nil), @@ -261,6 +265,7 @@ func TestDoStartRegisterContainerInstanceErrorTerminal(t *testing.T) { agent := &ecsAgent{ ctx: ctx, cfg: &cfg, + pauseLoader: mockPauseLoader, credentialProvider: aws_credentials.NewCredentials(mockCredentialsProvider), dockerClient: dockerClient, mobyPlugins: mockMobyPlugins, @@ -279,7 +284,10 @@ func TestDoStartRegisterContainerInstanceErrorNonTerminal(t *testing.T) { mockMobyPlugins := mock_mobypkgwrapper.NewMockPlugins(ctrl) mockCredentialsProvider := app_mocks.NewMockProvider(ctrl) mockEC2Metadata := mock_ec2.NewMockEC2MetadataClient(ctrl) + mockPauseLoader := mock_pause.NewMockLoader(ctrl) + mockPauseLoader.EXPECT().IsLoaded(gomock.Any()).Return(false, nil).AnyTimes() + mockPauseLoader.EXPECT().LoadImage(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() gomock.InOrder( dockerClient.EXPECT().SupportedVersions().Return(apiVersions), mockCredentialsProvider.EXPECT().Retrieve().Return(aws_credentials.Value{}, nil), @@ -302,6 +310,7 @@ func TestDoStartRegisterContainerInstanceErrorNonTerminal(t *testing.T) { ctx: ctx, cfg: &cfg, dockerClient: dockerClient, + pauseLoader:mockPauseLoader, credentialProvider: aws_credentials.NewCredentials(mockCredentialsProvider), mobyPlugins: mockMobyPlugins, ec2MetadataClient: mockEC2Metadata, @@ -328,6 +337,10 @@ func TestDoStartRegisterAvailabilityZone(t *testing.T) { dockerClient.EXPECT().Version(gomock.Any(), gomock.Any()).AnyTimes() mockCredentialsProvider := app_mocks.NewMockProvider(ctrl) containermetadata := mock_containermetadata.NewMockManager(ctrl) + mockPauseLoader := mock_pause.NewMockLoader(ctrl) + + mockPauseLoader.EXPECT().IsLoaded(gomock.Any()).Return(false, nil).AnyTimes() + mockPauseLoader.EXPECT().LoadImage(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() imageManager.EXPECT().StartImageCleanupProcess(gomock.Any()).MaxTimes(1) dockerClient.EXPECT().ListContainers(gomock.Any(), gomock.Any(), gomock.Any()).Return( dockerapi.ListContainersResponse{}).AnyTimes() @@ -373,6 +386,7 @@ func TestDoStartRegisterAvailabilityZone(t *testing.T) { ctx: ctx, cfg: &cfg, dockerClient: dockerClient, + pauseLoader:mockPauseLoader, credentialProvider: aws_credentials.NewCredentials(mockCredentialsProvider), mobyPlugins: mockMobyPlugins, metadataManager: containermetadata, @@ -399,6 +413,10 @@ func TestNewTaskEngineRestoreFromCheckpointNoEC2InstanceIDToLoadHappyPath(t *tes defer ctrl.Finish() ec2MetadataClient := mock_ec2.NewMockEC2MetadataClient(ctrl) + mockPauseLoader := mock_pause.NewMockLoader(ctrl) + + mockPauseLoader.EXPECT().IsLoaded(gomock.Any()).Return(false, nil).AnyTimes() + mockPauseLoader.EXPECT().LoadImage(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() cfg := getTestConfig() cfg.Checkpoint = true expectedInstanceID := "inst-1" @@ -428,6 +446,7 @@ func TestNewTaskEngineRestoreFromCheckpointNoEC2InstanceIDToLoadHappyPath(t *tes ctx: ctx, cfg: &cfg, dockerClient: dockerClient, + pauseLoader:mockPauseLoader, stateManagerFactory: stateManagerFactory, ec2MetadataClient: ec2MetadataClient, saveableOptionFactory: saveableOptionFactory, @@ -446,6 +465,10 @@ func TestNewTaskEngineRestoreFromCheckpointPreviousEC2InstanceIDLoadedHappyPath( defer ctrl.Finish() ec2MetadataClient := mock_ec2.NewMockEC2MetadataClient(ctrl) + mockPauseLoader := mock_pause.NewMockLoader(ctrl) + + mockPauseLoader.EXPECT().IsLoaded(gomock.Any()).Return(false, nil).AnyTimes() + mockPauseLoader.EXPECT().LoadImage(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() cfg := getTestConfig() cfg.Checkpoint = true expectedInstanceID := "inst-1" @@ -486,6 +509,7 @@ func TestNewTaskEngineRestoreFromCheckpointPreviousEC2InstanceIDLoadedHappyPath( ctx: ctx, cfg: &cfg, dockerClient: dockerClient, + pauseLoader:mockPauseLoader, stateManagerFactory: stateManagerFactory, ec2MetadataClient: ec2MetadataClient, saveableOptionFactory: saveableOptionFactory, @@ -505,6 +529,10 @@ func TestNewTaskEngineRestoreFromCheckpointClusterIDMismatch(t *testing.T) { defer ctrl.Finish() ec2MetadataClient := mock_ec2.NewMockEC2MetadataClient(ctrl) + mockPauseLoader := mock_pause.NewMockLoader(ctrl) + + mockPauseLoader.EXPECT().IsLoaded(gomock.Any()).Return(false, nil).AnyTimes() + mockPauseLoader.EXPECT().LoadImage(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() cfg := getTestConfig() cfg.Checkpoint = true cfg.Cluster = "default" @@ -541,6 +569,7 @@ func TestNewTaskEngineRestoreFromCheckpointClusterIDMismatch(t *testing.T) { ctx: ctx, cfg: &cfg, dockerClient: dockerClient, + pauseLoader:mockPauseLoader, stateManagerFactory: stateManagerFactory, ec2MetadataClient: ec2MetadataClient, saveableOptionFactory: saveableOptionFactory, @@ -559,6 +588,10 @@ func TestNewTaskEngineRestoreFromCheckpointNewStateManagerError(t *testing.T) { cfg := getTestConfig() cfg.Checkpoint = true + + mockPauseLoader := mock_pause.NewMockLoader(ctrl) + mockPauseLoader.EXPECT().IsLoaded(gomock.Any()).Return(false, nil).AnyTimes() + mockPauseLoader.EXPECT().LoadImage(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() gomock.InOrder( saveableOptionFactory.EXPECT().AddSaveable("ContainerInstanceArn", gomock.Any()).Return(nil), saveableOptionFactory.EXPECT().AddSaveable("Cluster", gomock.Any()).Return(nil), @@ -578,6 +611,7 @@ func TestNewTaskEngineRestoreFromCheckpointNewStateManagerError(t *testing.T) { ctx: ctx, cfg: &cfg, dockerClient: dockerClient, + pauseLoader:mockPauseLoader, stateManagerFactory: stateManagerFactory, saveableOptionFactory: saveableOptionFactory, } @@ -596,6 +630,10 @@ func TestNewTaskEngineRestoreFromCheckpointStateLoadError(t *testing.T) { stateManager := mock_statemanager.NewMockStateManager(ctrl) cfg := getTestConfig() cfg.Checkpoint = true + mockPauseLoader := mock_pause.NewMockLoader(ctrl) + + mockPauseLoader.EXPECT().IsLoaded(gomock.Any()).Return(false, nil).AnyTimes() + mockPauseLoader.EXPECT().LoadImage(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() gomock.InOrder( saveableOptionFactory.EXPECT().AddSaveable("ContainerInstanceArn", gomock.Any()).Return(nil), saveableOptionFactory.EXPECT().AddSaveable("Cluster", gomock.Any()).Return(nil), @@ -615,6 +653,7 @@ func TestNewTaskEngineRestoreFromCheckpointStateLoadError(t *testing.T) { ctx: ctx, cfg: &cfg, dockerClient: dockerClient, + pauseLoader:mockPauseLoader, stateManagerFactory: stateManagerFactory, saveableOptionFactory: saveableOptionFactory, } @@ -634,6 +673,10 @@ func TestNewTaskEngineRestoreFromCheckpoint(t *testing.T) { cfg := getTestConfig() cfg.Checkpoint = true expectedInstanceID := "inst-1" + mockPauseLoader := mock_pause.NewMockLoader(ctrl) + + mockPauseLoader.EXPECT().IsLoaded(gomock.Any()).Return(false, nil).AnyTimes() + mockPauseLoader.EXPECT().LoadImage(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() gomock.InOrder( saveableOptionFactory.EXPECT().AddSaveable("ContainerInstanceArn", gomock.Any()).Return(nil), saveableOptionFactory.EXPECT().AddSaveable("Cluster", gomock.Any()).Return(nil), @@ -656,6 +699,7 @@ func TestNewTaskEngineRestoreFromCheckpoint(t *testing.T) { dockerClient: dockerClient, stateManagerFactory: stateManagerFactory, ec2MetadataClient: ec2MetadataClient, + pauseLoader:mockPauseLoader, saveableOptionFactory: saveableOptionFactory, } @@ -718,7 +762,10 @@ func TestReregisterContainerInstanceHappyPath(t *testing.T) { mockCredentialsProvider := app_mocks.NewMockProvider(ctrl) mockMobyPlugins := mock_mobypkgwrapper.NewMockPlugins(ctrl) mockEC2Metadata := mock_ec2.NewMockEC2MetadataClient(ctrl) + mockPauseLoader := mock_pause.NewMockLoader(ctrl) + mockPauseLoader.EXPECT().IsLoaded(gomock.Any()).Return(false, nil).AnyTimes() + mockPauseLoader.EXPECT().LoadImage(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() gomock.InOrder( mockCredentialsProvider.EXPECT().Retrieve().Return(aws_credentials.Value{}, nil), mockDockerClient.EXPECT().SupportedVersions().Return(nil), @@ -741,6 +788,7 @@ func TestReregisterContainerInstanceHappyPath(t *testing.T) { ctx: ctx, cfg: &cfg, dockerClient: mockDockerClient, + pauseLoader:mockPauseLoader, credentialProvider: aws_credentials.NewCredentials(mockCredentialsProvider), mobyPlugins: mockMobyPlugins, ec2MetadataClient: mockEC2Metadata, @@ -762,7 +810,10 @@ func TestReregisterContainerInstanceInstanceTypeChanged(t *testing.T) { mockCredentialsProvider := app_mocks.NewMockProvider(ctrl) mockMobyPlugins := mock_mobypkgwrapper.NewMockPlugins(ctrl) mockEC2Metadata := mock_ec2.NewMockEC2MetadataClient(ctrl) + mockPauseLoader := mock_pause.NewMockLoader(ctrl) + mockPauseLoader.EXPECT().IsLoaded(gomock.Any()).Return(false, nil).AnyTimes() + mockPauseLoader.EXPECT().LoadImage(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() gomock.InOrder( mockCredentialsProvider.EXPECT().Retrieve().Return(aws_credentials.Value{}, nil), mockDockerClient.EXPECT().SupportedVersions().Return(nil), @@ -786,6 +837,7 @@ func TestReregisterContainerInstanceInstanceTypeChanged(t *testing.T) { ctx: ctx, cfg: &cfg, dockerClient: mockDockerClient, + pauseLoader:mockPauseLoader, credentialProvider: aws_credentials.NewCredentials(mockCredentialsProvider), ec2MetadataClient: mockEC2Metadata, mobyPlugins: mockMobyPlugins, @@ -808,6 +860,10 @@ func TestReregisterContainerInstanceAttributeError(t *testing.T) { mockCredentialsProvider := app_mocks.NewMockProvider(ctrl) mockMobyPlugins := mock_mobypkgwrapper.NewMockPlugins(ctrl) mockEC2Metadata := mock_ec2.NewMockEC2MetadataClient(ctrl) + mockPauseLoader := mock_pause.NewMockLoader(ctrl) + + mockPauseLoader.EXPECT().IsLoaded(gomock.Any()).Return(false, nil).AnyTimes() + mockPauseLoader.EXPECT().LoadImage(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() gomock.InOrder( mockCredentialsProvider.EXPECT().Retrieve().Return(aws_credentials.Value{}, nil), @@ -831,6 +887,7 @@ func TestReregisterContainerInstanceAttributeError(t *testing.T) { cfg: &cfg, ec2MetadataClient: mockEC2Metadata, dockerClient: mockDockerClient, + pauseLoader:mockPauseLoader, credentialProvider: aws_credentials.NewCredentials(mockCredentialsProvider), mobyPlugins: mockMobyPlugins, } @@ -852,7 +909,10 @@ func TestReregisterContainerInstanceNonTerminalError(t *testing.T) { mockCredentialsProvider := app_mocks.NewMockProvider(ctrl) mockMobyPlugins := mock_mobypkgwrapper.NewMockPlugins(ctrl) mockEC2Metadata := mock_ec2.NewMockEC2MetadataClient(ctrl) + mockPauseLoader := mock_pause.NewMockLoader(ctrl) + mockPauseLoader.EXPECT().IsLoaded(gomock.Any()).Return(false, nil).AnyTimes() + mockPauseLoader.EXPECT().LoadImage(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() gomock.InOrder( mockCredentialsProvider.EXPECT().Retrieve().Return(aws_credentials.Value{}, nil), mockDockerClient.EXPECT().SupportedVersions().Return(nil), @@ -875,6 +935,7 @@ func TestReregisterContainerInstanceNonTerminalError(t *testing.T) { cfg: &cfg, dockerClient: mockDockerClient, ec2MetadataClient: mockEC2Metadata, + pauseLoader:mockPauseLoader, credentialProvider: aws_credentials.NewCredentials(mockCredentialsProvider), mobyPlugins: mockMobyPlugins, } @@ -897,6 +958,10 @@ func TestRegisterContainerInstanceWhenContainerInstanceARNIsNotSetHappyPath(t *t mockMobyPlugins := mock_mobypkgwrapper.NewMockPlugins(ctrl) mockEC2Metadata := mock_ec2.NewMockEC2MetadataClient(ctrl) + mockPauseLoader := mock_pause.NewMockLoader(ctrl) + + mockPauseLoader.EXPECT().IsLoaded(gomock.Any()).Return(false, nil).AnyTimes() + mockPauseLoader.EXPECT().LoadImage(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() gomock.InOrder( mockCredentialsProvider.EXPECT().Retrieve().Return(aws_credentials.Value{}, nil), mockDockerClient.EXPECT().SupportedVersions().Return(nil), @@ -919,6 +984,7 @@ func TestRegisterContainerInstanceWhenContainerInstanceARNIsNotSetHappyPath(t *t cfg: &cfg, dockerClient: mockDockerClient, ec2MetadataClient: mockEC2Metadata, + pauseLoader:mockPauseLoader, credentialProvider: aws_credentials.NewCredentials(mockCredentialsProvider), mobyPlugins: mockMobyPlugins, } @@ -938,7 +1004,10 @@ func TestRegisterContainerInstanceWhenContainerInstanceARNIsNotSetCanRetryError( mockCredentialsProvider := app_mocks.NewMockProvider(ctrl) mockMobyPlugins := mock_mobypkgwrapper.NewMockPlugins(ctrl) mockEC2Metadata := mock_ec2.NewMockEC2MetadataClient(ctrl) + mockPauseLoader := mock_pause.NewMockLoader(ctrl) + mockPauseLoader.EXPECT().IsLoaded(gomock.Any()).Return(false, nil).AnyTimes() + mockPauseLoader.EXPECT().LoadImage(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() retriableError := apierrors.NewRetriableError(apierrors.NewRetriable(true), errors.New("error")) gomock.InOrder( mockCredentialsProvider.EXPECT().Retrieve().Return(aws_credentials.Value{}, nil), @@ -962,6 +1031,7 @@ func TestRegisterContainerInstanceWhenContainerInstanceARNIsNotSetCanRetryError( cfg: &cfg, dockerClient: mockDockerClient, ec2MetadataClient: mockEC2Metadata, + pauseLoader:mockPauseLoader, credentialProvider: aws_credentials.NewCredentials(mockCredentialsProvider), mobyPlugins: mockMobyPlugins, } @@ -981,7 +1051,10 @@ func TestRegisterContainerInstanceWhenContainerInstanceARNIsNotSetCannotRetryErr mockCredentialsProvider := app_mocks.NewMockProvider(ctrl) mockMobyPlugins := mock_mobypkgwrapper.NewMockPlugins(ctrl) mockEC2Metadata := mock_ec2.NewMockEC2MetadataClient(ctrl) + mockPauseLoader := mock_pause.NewMockLoader(ctrl) + mockPauseLoader.EXPECT().IsLoaded(gomock.Any()).Return(false, nil).AnyTimes() + mockPauseLoader.EXPECT().LoadImage(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() cannotRetryError := apierrors.NewRetriableError(apierrors.NewRetriable(false), errors.New("error")) gomock.InOrder( mockCredentialsProvider.EXPECT().Retrieve().Return(aws_credentials.Value{}, nil), @@ -1005,6 +1078,7 @@ func TestRegisterContainerInstanceWhenContainerInstanceARNIsNotSetCannotRetryErr cfg: &cfg, ec2MetadataClient: mockEC2Metadata, dockerClient: mockDockerClient, + pauseLoader:mockPauseLoader, credentialProvider: aws_credentials.NewCredentials(mockCredentialsProvider), mobyPlugins: mockMobyPlugins, } @@ -1024,7 +1098,10 @@ func TestRegisterContainerInstanceWhenContainerInstanceARNIsNotSetAttributeError mockCredentialsProvider := app_mocks.NewMockProvider(ctrl) mockMobyPlugins := mock_mobypkgwrapper.NewMockPlugins(ctrl) mockEC2Metadata := mock_ec2.NewMockEC2MetadataClient(ctrl) + mockPauseLoader := mock_pause.NewMockLoader(ctrl) + mockPauseLoader.EXPECT().IsLoaded(gomock.Any()).Return(false, nil).AnyTimes() + mockPauseLoader.EXPECT().LoadImage(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() gomock.InOrder( mockCredentialsProvider.EXPECT().Retrieve().Return(aws_credentials.Value{}, nil), mockDockerClient.EXPECT().SupportedVersions().Return(nil), @@ -1047,6 +1124,7 @@ func TestRegisterContainerInstanceWhenContainerInstanceARNIsNotSetAttributeError cfg: &cfg, ec2MetadataClient: mockEC2Metadata, dockerClient: mockDockerClient, + pauseLoader:mockPauseLoader, credentialProvider: aws_credentials.NewCredentials(mockCredentialsProvider), mobyPlugins: mockMobyPlugins, } @@ -1064,7 +1142,10 @@ func TestRegisterContainerInstanceInvalidParameterTerminalError(t *testing.T) { mockCredentialsProvider := app_mocks.NewMockProvider(ctrl) mockMobyPlugins := mock_mobypkgwrapper.NewMockPlugins(ctrl) mockEC2Metadata := mock_ec2.NewMockEC2MetadataClient(ctrl) + mockPauseLoader := mock_pause.NewMockLoader(ctrl) + mockPauseLoader.EXPECT().IsLoaded(gomock.Any()).Return(false, nil).AnyTimes() + mockPauseLoader.EXPECT().LoadImage(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() gomock.InOrder( dockerClient.EXPECT().SupportedVersions().Return(apiVersions), mockCredentialsProvider.EXPECT().Retrieve().Return(aws_credentials.Value{}, nil), @@ -1086,6 +1167,7 @@ func TestRegisterContainerInstanceInvalidParameterTerminalError(t *testing.T) { ctx: ctx, ec2MetadataClient: mockEC2Metadata, cfg: &cfg, + pauseLoader:mockPauseLoader, credentialProvider: aws_credentials.NewCredentials(mockCredentialsProvider), dockerClient: dockerClient, mobyPlugins: mockMobyPlugins, diff --git a/agent/app/agent_unix.go b/agent/app/agent_unix.go index 1201fe3d425..b7f7d131edd 100644 --- a/agent/app/agent_unix.go +++ b/agent/app/agent_unix.go @@ -17,6 +17,7 @@ package app import ( "fmt" + "github.com/aws/amazon-ecs-agent/agent/eni/pause" asmfactory "github.com/aws/amazon-ecs-agent/agent/asm/factory" "github.com/aws/amazon-ecs-agent/agent/config" @@ -25,7 +26,6 @@ import ( "github.com/aws/amazon-ecs-agent/agent/ecscni" "github.com/aws/amazon-ecs-agent/agent/engine" "github.com/aws/amazon-ecs-agent/agent/engine/dockerstate" - "github.com/aws/amazon-ecs-agent/agent/eni/pause" "github.com/aws/amazon-ecs-agent/agent/eni/udevwrapper" "github.com/aws/amazon-ecs-agent/agent/eni/watcher" "github.com/aws/amazon-ecs-agent/agent/gpu" @@ -86,8 +86,7 @@ func (agent *ecsAgent) initializeTaskENIDependencies(state dockerstate.TaskEngin return err, true } - // Load the pause container's image from the 'disk' - if _, err := agent.pauseLoader.LoadImage(agent.ctx, agent.cfg, agent.dockerClient); err != nil { + if isLoaded, err := agent.pauseLoader.IsLoaded(agent.dockerClient); err != nil || !isLoaded { if pause.IsNoSuchFileError(err) || pause.UnsupportedPlatform(err) { // If the pause container's image tarball doesn't exist or if the // invocation is done for an unsupported platform, we cannot recover. @@ -251,3 +250,15 @@ func (agent *ecsAgent) getPlatformDevices() []*ecs.PlatformDevice { } return nil } + +func (agent *ecsAgent) loadPauseContainer() error { + + if agent.pauseLoader == nil { + return errors.New("Pause Loader is not initialized") + } + + // Load the pause container's image from the 'disk' + _, err := agent.pauseLoader.LoadImage(agent.ctx, agent.cfg, agent.dockerClient) + + return err +} diff --git a/agent/app/agent_unix_test.go b/agent/app/agent_unix_test.go index 089fa43437f..cc0c91e7ed5 100644 --- a/agent/app/agent_unix_test.go +++ b/agent/app/agent_unix_test.go @@ -66,6 +66,7 @@ func TestDoStartHappyPath(t *testing.T) { var discoverEndpointsInvoked sync.WaitGroup discoverEndpointsInvoked.Add(2) containerChangeEvents := make(chan dockerapi.DockerContainerChangeEvent) + mockPauseLoader := mock_pause.NewMockLoader(ctrl) // These calls are expected to happen, but cannot be ordered as they are // invoked via go routines, which will lead to occasional test failues @@ -87,6 +88,8 @@ func TestDoStartHappyPath(t *testing.T) { client.EXPECT().DiscoverTelemetryEndpoint(gomock.Any()).Return( "tele-endpoint", nil).AnyTimes() ec2MetadataClient.EXPECT().OutpostARN().Return("", nil) + mockPauseLoader.EXPECT().IsLoaded(gomock.Any()).Return(false, nil).AnyTimes() + mockPauseLoader.EXPECT().LoadImage(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() gomock.InOrder( mockCredentialsProvider.EXPECT().Retrieve().Return(credentials.Value{}, nil), @@ -112,6 +115,7 @@ func TestDoStartHappyPath(t *testing.T) { cfg: &cfg, credentialProvider: credentials.NewCredentials(mockCredentialsProvider), dockerClient: dockerClient, + pauseLoader:mockPauseLoader, terminationHandler: func(saver statemanager.Saver, taskEngine engine.TaskEngine) {}, mobyPlugins: mockMobyPlugins, ec2MetadataClient: ec2MetadataClient, @@ -173,6 +177,7 @@ func TestDoStartTaskENIHappyPath(t *testing.T) { mockMetadata.EXPECT().OutpostARN().Return("", nil) gomock.InOrder( + mockPauseLoader.EXPECT().LoadImage(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil), mockOS.EXPECT().Getpid().Return(10), mockMetadata.EXPECT().PrimaryENIMAC().Return(mac, nil), mockMetadata.EXPECT().VPCID(mac).Return(vpcID, nil), @@ -182,7 +187,7 @@ func TestDoStartTaskENIHappyPath(t *testing.T) { cniClient.EXPECT().Capabilities(ecscni.ECSIPAMPluginName).Return(cniCapabilities, nil), cniClient.EXPECT().Capabilities(ecscni.ECSAppMeshPluginName).Return(cniCapabilities, nil), cniClient.EXPECT().Capabilities(ecscni.ECSBranchENIPluginName).Return(cniCapabilities, nil), - mockPauseLoader.EXPECT().LoadImage(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil), + mockPauseLoader.EXPECT().IsLoaded(gomock.Any()).Return(true, nil), state.EXPECT().ENIByMac(gomock.Any()).Return(nil, false).AnyTimes(), mockCredentialsProvider.EXPECT().Retrieve().Return(credentials.Value{}, nil), dockerClient.EXPECT().SupportedVersions().Return(nil), @@ -495,7 +500,7 @@ func TestInitializeTaskENIDependenciesPauseLoaderError(t *testing.T) { cniClient.EXPECT().Capabilities(ecscni.ECSBridgePluginName).Return(cniCapabilities, nil), cniClient.EXPECT().Capabilities(ecscni.ECSIPAMPluginName).Return(cniCapabilities, nil), cniClient.EXPECT().Capabilities(ecscni.ECSAppMeshPluginName).Return(cniCapabilities, nil), - mockPauseLoader.EXPECT().LoadImage(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, loadErr), + mockPauseLoader.EXPECT().IsLoaded(gomock.Any()).Return(false, loadErr), ) cfg := getTestConfig() agent := &ecsAgent{ diff --git a/agent/app/agent_unspecified.go b/agent/app/agent_unspecified.go index 155d38d8a71..48350f3164b 100644 --- a/agent/app/agent_unspecified.go +++ b/agent/app/agent_unspecified.go @@ -49,3 +49,7 @@ func (agent *ecsAgent) initializeGPUManager() error { func (agent *ecsAgent) getPlatformDevices() []*ecs.PlatformDevice { return nil } + +func (agent *ecsAgent) loadPauseContainer() error { + return nil +} diff --git a/agent/app/agent_windows.go b/agent/app/agent_windows.go index 1b74ae27c07..28ef524baf4 100644 --- a/agent/app/agent_windows.go +++ b/agent/app/agent_windows.go @@ -277,3 +277,7 @@ func (agent *ecsAgent) initializeGPUManager() error { func (agent *ecsAgent) getPlatformDevices() []*ecs.PlatformDevice { return nil } + +func (agent *ecsAgent) loadPauseContainer() error { + return nil +} \ No newline at end of file diff --git a/agent/eni/pause/load.go b/agent/eni/pause/load.go index 87921e315f8..1a1174c2dd3 100644 --- a/agent/eni/pause/load.go +++ b/agent/eni/pause/load.go @@ -25,6 +25,7 @@ import ( // to facilitate mocking and testing of the LoadImage method type Loader interface { LoadImage(ctx context.Context, cfg *config.Config, dockerClient dockerapi.DockerClient) (*types.ImageInspect, error) + IsLoaded(dockerClient dockerapi.DockerClient) (bool, error) } type loader struct{} diff --git a/agent/eni/pause/mocks/load_mocks.go b/agent/eni/pause/mocks/load_mocks.go index d7fe182682f..41e961631dc 100644 --- a/agent/eni/pause/mocks/load_mocks.go +++ b/agent/eni/pause/mocks/load_mocks.go @@ -13,19 +13,18 @@ // // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/aws/amazon-ecs-agent/agent/eni/pause (interfaces: Loader) +// Source: load.go // Package mock_pause is a generated GoMock package. package mock_pause import ( context "context" - reflect "reflect" - config "github.com/aws/amazon-ecs-agent/agent/config" dockerapi "github.com/aws/amazon-ecs-agent/agent/dockerclient/dockerapi" types "github.com/docker/docker/api/types" gomock "github.com/golang/mock/gomock" + reflect "reflect" ) // MockLoader is a mock of Loader interface @@ -52,16 +51,31 @@ func (m *MockLoader) EXPECT() *MockLoaderMockRecorder { } // LoadImage mocks base method -func (m *MockLoader) LoadImage(arg0 context.Context, arg1 *config.Config, arg2 dockerapi.DockerClient) (*types.ImageInspect, error) { +func (m *MockLoader) LoadImage(ctx context.Context, cfg *config.Config, dockerClient dockerapi.DockerClient) (*types.ImageInspect, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "LoadImage", arg0, arg1, arg2) + ret := m.ctrl.Call(m, "LoadImage", ctx, cfg, dockerClient) ret0, _ := ret[0].(*types.ImageInspect) ret1, _ := ret[1].(error) return ret0, ret1 } // LoadImage indicates an expected call of LoadImage -func (mr *MockLoaderMockRecorder) LoadImage(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockLoaderMockRecorder) LoadImage(ctx, cfg, dockerClient interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadImage", reflect.TypeOf((*MockLoader)(nil).LoadImage), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadImage", reflect.TypeOf((*MockLoader)(nil).LoadImage), ctx, cfg, dockerClient) +} + +// IsLoaded mocks base method +func (m *MockLoader) IsLoaded(dockerClient dockerapi.DockerClient) (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsLoaded", dockerClient) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 } + +// IsLoaded indicates an expected call of IsLoaded +func (mr *MockLoaderMockRecorder) IsLoaded(dockerClient interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsLoaded", reflect.TypeOf((*MockLoader)(nil).IsLoaded), dockerClient) +} \ No newline at end of file diff --git a/agent/eni/pause/pause_linux.go b/agent/eni/pause/pause_linux.go index bca153d0112..0710a2551f4 100644 --- a/agent/eni/pause/pause_linux.go +++ b/agent/eni/pause/pause_linux.go @@ -40,6 +40,22 @@ func (*loader) LoadImage(ctx context.Context, cfg *config.Config, dockerClient d config.DefaultPauseContainerImageName, config.DefaultPauseContainerTag, dockerClient) } +func (*loader) IsLoaded(dockerClient dockerapi.DockerClient) (bool, error) { + image, err := getPauseContainerImage( + config.DefaultPauseContainerImageName, config.DefaultPauseContainerTag, dockerClient) + + if err != nil { + return false, errors.Wrapf(err, + "pause container inspect: failed to inspect image: %s", config.DefaultPauseContainerImageName) + } + + if image == nil || image.ID == "" { + return false, nil + } + + return true, nil +} + func loadFromFile(ctx context.Context, path string, dockerClient dockerapi.DockerClient, fs os.FileSystem) error { pauseContainerReader, err := fs.Open(path) if err != nil { diff --git a/agent/eni/pause/pause_linux_test.go b/agent/eni/pause/pause_linux_test.go index a72851ef37a..ffc4dc77645 100644 --- a/agent/eni/pause/pause_linux_test.go +++ b/agent/eni/pause/pause_linux_test.go @@ -1,4 +1,4 @@ -// +build linux,unit +//+build linux,unit // Copyright 2017-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. // @@ -154,3 +154,73 @@ func TestGetPauseContainerHappyPath(t *testing.T) { _, err = getPauseContainerImage(pauseName, pauseTag, client) assert.NoError(t, err) } + +func TestIsLoadedHappyPath(t *testing.T) { + ctrl := gomock.NewController(t) + pauseLoader := New() + defer ctrl.Finish() + + // Docker SDK tests + mockDockerSDK := mock_sdkclient.NewMockClient(ctrl) + mockDockerSDK.EXPECT().Ping(gomock.Any()).Return(types.Ping{}, nil) + sdkFactory := mock_sdkclientfactory.NewMockFactory(ctrl) + sdkFactory.EXPECT().GetDefaultClient().AnyTimes().Return(mockDockerSDK, nil) + + ctx, cancel := context.WithCancel(context.TODO()) + defer cancel() + + client, err := dockerapi.NewDockerGoClient(sdkFactory, &defaultConfig, ctx) + assert.NoError(t, err) + mockDockerSDK.EXPECT().ImageInspectWithRaw(gomock.Any(), gomock.Any()).Return(types.ImageInspect{ID: "test123"}, nil, nil) + + isLoaded, err := pauseLoader.IsLoaded(client) + assert.NoError(t, err) + assert.True(t, isLoaded) +} + +func TestIsLoadedNotLoaded(t *testing.T) { + ctrl := gomock.NewController(t) + pauseLoader := New() + defer ctrl.Finish() + + // Docker SDK tests + mockDockerSDK := mock_sdkclient.NewMockClient(ctrl) + mockDockerSDK.EXPECT().Ping(gomock.Any()).Return(types.Ping{}, nil) + sdkFactory := mock_sdkclientfactory.NewMockFactory(ctrl) + sdkFactory.EXPECT().GetDefaultClient().AnyTimes().Return(mockDockerSDK, nil) + + ctx, cancel := context.WithCancel(context.TODO()) + defer cancel() + + client, err := dockerapi.NewDockerGoClient(sdkFactory, &defaultConfig, ctx) + assert.NoError(t, err) + mockDockerSDK.EXPECT().ImageInspectWithRaw(gomock.Any(), gomock.Any()).Return(types.ImageInspect{}, nil, nil) + + isLoaded, err := pauseLoader.IsLoaded(client) + assert.NoError(t, err) + assert.False(t, isLoaded) +} + +func TestIsLoadedError(t *testing.T) { + ctrl := gomock.NewController(t) + pauseLoader := New() + defer ctrl.Finish() + + // Docker SDK tests + mockDockerSDK := mock_sdkclient.NewMockClient(ctrl) + mockDockerSDK.EXPECT().Ping(gomock.Any()).Return(types.Ping{}, nil) + sdkFactory := mock_sdkclientfactory.NewMockFactory(ctrl) + sdkFactory.EXPECT().GetDefaultClient().AnyTimes().Return(mockDockerSDK, nil) + + ctx, cancel := context.WithCancel(context.TODO()) + defer cancel() + + client, err := dockerapi.NewDockerGoClient(sdkFactory, &defaultConfig, ctx) + assert.NoError(t, err) + mockDockerSDK.EXPECT().ImageInspectWithRaw(gomock.Any(), gomock.Any()).Return( + types.ImageInspect{}, nil, errors.New("error")) + + isLoaded, err := pauseLoader.IsLoaded(client) + assert.Error(t, err) + assert.False(t, isLoaded) +} diff --git a/agent/eni/pause/pause_unsupported.go b/agent/eni/pause/pause_unsupported.go index 0c96eedee6b..3125ca13795 100644 --- a/agent/eni/pause/pause_unsupported.go +++ b/agent/eni/pause/pause_unsupported.go @@ -31,3 +31,9 @@ func (*loader) LoadImage(ctx context.Context, cfg *config.Config, dockerClient d "pause container load: unsupported platform: %s/%s", runtime.GOOS, runtime.GOARCH)) } + +func (*loader) IsLoaded(dockerClient dockerapi.DockerClient) (bool, error) { + return false, NewUnsupportedPlatformError(errors.Errorf( + "pause container isloaded: unsupported platform: %s/%s", + runtime.GOOS, runtime.GOARCH)) +}