diff --git a/agent/app/agent.go b/agent/app/agent.go index eeea01ec852..10fe3c9446b 100644 --- a/agent/app/agent.go +++ b/agent/app/agent.go @@ -260,9 +260,23 @@ func (agent *ecsAgent) doStart(containerChangeEventStream *eventstream.EventStre return exitcodes.ExitTerminal } + loadPauseErr := agent.loadPauseContainer() + if loadPauseErr != nil { + seelog.Errorf("Failed to load pause container: %v", loadPauseErr) + } + var vpcSubnetAttributes []*ecs.Attribute // Check if Task ENI is enabled if agent.cfg.TaskENIEnabled { + // check pause container image load + if loadPauseErr != nil { + if pause.IsNoSuchFileError(loadPauseErr) || pause.UnsupportedPlatform(loadPauseErr) { + return exitcodes.ExitTerminal + } else { + return exitcodes.ExitError + } + } + err, terminal := agent.initializeTaskENIDependencies(state, taskEngine) switch err { case nil: diff --git a/agent/app/agent_capability_test.go b/agent/app/agent_capability_test.go index 4e553a7d867..7ffd610b87f 100644 --- a/agent/app/agent_capability_test.go +++ b/agent/app/agent_capability_test.go @@ -20,6 +20,8 @@ import ( "strings" "testing" + mock_pause "github.com/aws/amazon-ecs-agent/agent/eni/pause/mocks" + "github.com/aws/amazon-ecs-agent/agent/ecs_client/model/ecs" "context" @@ -46,6 +48,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 +64,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 +147,7 @@ func TestCapabilities(t *testing.T) { cfg: conf, dockerClient: client, cniClient: cniClient, + pauseLoader: mockPauseLoader, credentialProvider: aws_credentials.NewCredentials(mockCredentialsProvider), mobyPlugins: mockMobyPlugins, } @@ -169,12 +175,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 +221,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 +231,7 @@ func TestCapabilitiesTaskIAMRoleForSupportedDockerVersion(t *testing.T) { ctx: ctx, cfg: conf, dockerClient: client, + pauseLoader: mockPauseLoader, mobyPlugins: mockMobyPlugins, } capabilities, err := agent.capabilities() @@ -250,6 +264,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 +274,7 @@ func TestCapabilitiesTaskIAMRoleForUnSupportedDockerVersion(t *testing.T) { ctx: ctx, cfg: conf, dockerClient: client, + pauseLoader: mockPauseLoader, mobyPlugins: mockMobyPlugins, } @@ -290,6 +308,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 +318,7 @@ func TestCapabilitiesTaskIAMRoleNetworkHostForSupportedDockerVersion(t *testing. ctx: ctx, cfg: conf, dockerClient: client, + pauseLoader: mockPauseLoader, mobyPlugins: mockMobyPlugins, } @@ -330,6 +352,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 +362,7 @@ func TestCapabilitiesTaskIAMRoleNetworkHostForUnSupportedDockerVersion(t *testin ctx: ctx, cfg: conf, dockerClient: client, + pauseLoader: mockPauseLoader, mobyPlugins: mockMobyPlugins, } @@ -359,6 +385,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 +395,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 +432,7 @@ func TestAWSVPCBlockInstanceMetadataWhenTaskENIIsDisabled(t *testing.T) { cfg: conf, dockerClient: client, cniClient: cniClient, + pauseLoader: mockPauseLoader, credentialProvider: aws_credentials.NewCredentials(mockCredentialsProvider), mobyPlugins: mockMobyPlugins, } @@ -443,6 +472,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 +483,7 @@ func TestCapabilitiesExecutionRoleAWSLogs(t *testing.T) { cfg: conf, dockerClient: client, cniClient: cniClient, + pauseLoader: mockPauseLoader, mobyPlugins: mockMobyPlugins, } @@ -474,6 +507,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 +523,7 @@ func TestCapabilitiesTaskResourceLimit(t *testing.T) { ctx: ctx, cfg: conf, dockerClient: client, + pauseLoader: mockPauseLoader, mobyPlugins: mockMobyPlugins, } @@ -514,6 +550,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 +566,7 @@ func TestCapabilitesTaskResourceLimitDisabledByMissingDockerVersion(t *testing.T ctx: ctx, cfg: conf, dockerClient: client, + pauseLoader: mockPauseLoader, mobyPlugins: mockMobyPlugins, } @@ -553,6 +592,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 +604,7 @@ func TestCapabilitesTaskResourceLimitErrorCase(t *testing.T) { agent := &ecsAgent{ ctx: ctx, cfg: conf, + pauseLoader: mockPauseLoader, dockerClient: client, } @@ -586,6 +628,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).AnyTimes() + ctx, cancel := context.WithCancel(context.TODO()) // Cancel the context to cancel async routines defer cancel() @@ -593,6 +638,7 @@ func TestCapabilitiesContainerHealth(t *testing.T) { ctx: ctx, cfg: &config.Config{}, dockerClient: client, + pauseLoader: mockPauseLoader, mobyPlugins: mockMobyPlugins, } @@ -623,6 +669,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).AnyTimes() + ctx, cancel := context.WithCancel(context.TODO()) // Cancel the context to cancel async routines defer cancel() @@ -630,6 +679,7 @@ func TestCapabilitiesContainerHealthDisabled(t *testing.T) { ctx: ctx, cfg: &config.Config{DisableDockerHealthCheck: true}, dockerClient: client, + pauseLoader: mockPauseLoader, mobyPlugins: mockMobyPlugins, } @@ -651,6 +701,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).AnyTimes() gomock.InOrder( client.EXPECT().SupportedVersions().Return(versionList), client.EXPECT().KnownVersions().Return(versionList), @@ -665,6 +717,7 @@ func TestCapabilitesListPluginsErrorCase(t *testing.T) { ctx: ctx, cfg: &config.Config{}, dockerClient: client, + pauseLoader: mockPauseLoader, mobyPlugins: mockMobyPlugins, } @@ -685,6 +738,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).AnyTimes() gomock.InOrder( client.EXPECT().SupportedVersions().Return(versionList), client.EXPECT().KnownVersions().Return(versionList), @@ -699,6 +754,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..c5739a59394 100644 --- a/agent/app/agent_capability_unix_test.go +++ b/agent/app/agent_capability_unix_test.go @@ -17,10 +17,13 @@ package app import ( "context" + "errors" "os" "path/filepath" "testing" + mock_pause "github.com/aws/amazon-ecs-agent/agent/eni/pause/mocks" + app_mocks "github.com/aws/amazon-ecs-agent/agent/app/mocks" "github.com/aws/amazon-ecs-agent/agent/config" "github.com/aws/amazon-ecs-agent/agent/dockerclient" @@ -46,6 +49,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 +66,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 +135,7 @@ func TestVolumeDriverCapabilitiesUnix(t *testing.T) { cfg: conf, dockerClient: client, cniClient: cniClient, + pauseLoader: mockPauseLoader, credentialProvider: aws_credentials.NewCredentials(mockCredentialsProvider), mobyPlugins: mockMobyPlugins, } @@ -149,11 +155,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 +211,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 +236,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 +289,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 +315,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 +383,7 @@ func TestENITrunkingCapabilitiesUnix(t *testing.T) { cfg: conf, dockerClient: client, cniClient: cniClient, + pauseLoader: mockPauseLoader, credentialProvider: aws_credentials.NewCredentials(mockCredentialsProvider), mobyPlugins: mockMobyPlugins, } @@ -389,12 +404,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 +464,7 @@ func TestNoENITrunkingCapabilitiesUnix(t *testing.T) { cfg: conf, dockerClient: client, cniClient: cniClient, + pauseLoader: mockPauseLoader, credentialProvider: aws_credentials.NewCredentials(mockCredentialsProvider), mobyPlugins: mockMobyPlugins, } @@ -466,10 +484,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 +552,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 +657,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 +728,7 @@ func TestAppMeshCapabilitiesUnix(t *testing.T) { ctx: ctx, cfg: conf, dockerClient: client, + pauseLoader: mockPauseLoader, credentialProvider: aws_credentials.NewCredentials(mockCredentialsProvider), mobyPlugins: mockMobyPlugins, } @@ -644,10 +753,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 +778,7 @@ func TestTaskEIACapabilitiesNoOptimizedCPU(t *testing.T) { ctx: ctx, cfg: conf, dockerClient: client, + pauseLoader: mockPauseLoader, credentialProvider: aws_credentials.NewCredentials(mockCredentialsProvider), mobyPlugins: mockMobyPlugins, } @@ -683,6 +795,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 +806,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 +826,7 @@ func TestTaskEIACapabilitiesWithOptimizedCPU(t *testing.T) { ctx: ctx, cfg: conf, dockerClient: client, + pauseLoader: mockPauseLoader, credentialProvider: aws_credentials.NewCredentials(mockCredentialsProvider), mobyPlugins: mockMobyPlugins, } @@ -730,10 +845,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 +925,7 @@ func TestAWSLoggingDriverAndLogRouterCapabilitiesUnix(t *testing.T) { ctx: ctx, cfg: conf, dockerClient: client, + pauseLoader: mockPauseLoader, credentialProvider: aws_credentials.NewCredentials(mockCredentialsProvider), mobyPlugins: mockMobyPlugins, } @@ -828,10 +946,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 +971,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..91f297c7742 100644 --- a/agent/app/agent_test.go +++ b/agent/app/agent_test.go @@ -23,6 +23,8 @@ import ( "sync" "testing" + mock_pause "github.com/aws/amazon-ecs-agent/agent/eni/pause/mocks" + apierrors "github.com/aws/amazon-ecs-agent/agent/api/errors" mock_api "github.com/aws/amazon-ecs-agent/agent/api/mocks" mock_factory "github.com/aws/amazon-ecs-agent/agent/app/factory/mocks" @@ -238,7 +240,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 +266,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 +285,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 +311,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 +338,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 +387,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 +414,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 +447,7 @@ func TestNewTaskEngineRestoreFromCheckpointNoEC2InstanceIDToLoadHappyPath(t *tes ctx: ctx, cfg: &cfg, dockerClient: dockerClient, + pauseLoader: mockPauseLoader, stateManagerFactory: stateManagerFactory, ec2MetadataClient: ec2MetadataClient, saveableOptionFactory: saveableOptionFactory, @@ -446,6 +466,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 +510,7 @@ func TestNewTaskEngineRestoreFromCheckpointPreviousEC2InstanceIDLoadedHappyPath( ctx: ctx, cfg: &cfg, dockerClient: dockerClient, + pauseLoader: mockPauseLoader, stateManagerFactory: stateManagerFactory, ec2MetadataClient: ec2MetadataClient, saveableOptionFactory: saveableOptionFactory, @@ -505,6 +530,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 +570,7 @@ func TestNewTaskEngineRestoreFromCheckpointClusterIDMismatch(t *testing.T) { ctx: ctx, cfg: &cfg, dockerClient: dockerClient, + pauseLoader: mockPauseLoader, stateManagerFactory: stateManagerFactory, ec2MetadataClient: ec2MetadataClient, saveableOptionFactory: saveableOptionFactory, @@ -559,6 +589,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 +612,7 @@ func TestNewTaskEngineRestoreFromCheckpointNewStateManagerError(t *testing.T) { ctx: ctx, cfg: &cfg, dockerClient: dockerClient, + pauseLoader: mockPauseLoader, stateManagerFactory: stateManagerFactory, saveableOptionFactory: saveableOptionFactory, } @@ -596,6 +631,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 +654,7 @@ func TestNewTaskEngineRestoreFromCheckpointStateLoadError(t *testing.T) { ctx: ctx, cfg: &cfg, dockerClient: dockerClient, + pauseLoader: mockPauseLoader, stateManagerFactory: stateManagerFactory, saveableOptionFactory: saveableOptionFactory, } @@ -634,6 +674,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 +700,7 @@ func TestNewTaskEngineRestoreFromCheckpoint(t *testing.T) { dockerClient: dockerClient, stateManagerFactory: stateManagerFactory, ec2MetadataClient: ec2MetadataClient, + pauseLoader: mockPauseLoader, saveableOptionFactory: saveableOptionFactory, } @@ -718,7 +763,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 +789,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 +811,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 +838,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 +861,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 +888,7 @@ func TestReregisterContainerInstanceAttributeError(t *testing.T) { cfg: &cfg, ec2MetadataClient: mockEC2Metadata, dockerClient: mockDockerClient, + pauseLoader: mockPauseLoader, credentialProvider: aws_credentials.NewCredentials(mockCredentialsProvider), mobyPlugins: mockMobyPlugins, } @@ -852,7 +910,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 +936,7 @@ func TestReregisterContainerInstanceNonTerminalError(t *testing.T) { cfg: &cfg, dockerClient: mockDockerClient, ec2MetadataClient: mockEC2Metadata, + pauseLoader: mockPauseLoader, credentialProvider: aws_credentials.NewCredentials(mockCredentialsProvider), mobyPlugins: mockMobyPlugins, } @@ -897,6 +959,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 +985,7 @@ func TestRegisterContainerInstanceWhenContainerInstanceARNIsNotSetHappyPath(t *t cfg: &cfg, dockerClient: mockDockerClient, ec2MetadataClient: mockEC2Metadata, + pauseLoader: mockPauseLoader, credentialProvider: aws_credentials.NewCredentials(mockCredentialsProvider), mobyPlugins: mockMobyPlugins, } @@ -938,7 +1005,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 +1032,7 @@ func TestRegisterContainerInstanceWhenContainerInstanceARNIsNotSetCanRetryError( cfg: &cfg, dockerClient: mockDockerClient, ec2MetadataClient: mockEC2Metadata, + pauseLoader: mockPauseLoader, credentialProvider: aws_credentials.NewCredentials(mockCredentialsProvider), mobyPlugins: mockMobyPlugins, } @@ -981,7 +1052,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 +1079,7 @@ func TestRegisterContainerInstanceWhenContainerInstanceARNIsNotSetCannotRetryErr cfg: &cfg, ec2MetadataClient: mockEC2Metadata, dockerClient: mockDockerClient, + pauseLoader: mockPauseLoader, credentialProvider: aws_credentials.NewCredentials(mockCredentialsProvider), mobyPlugins: mockMobyPlugins, } @@ -1024,7 +1099,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 +1125,7 @@ func TestRegisterContainerInstanceWhenContainerInstanceARNIsNotSetAttributeError cfg: &cfg, ec2MetadataClient: mockEC2Metadata, dockerClient: mockDockerClient, + pauseLoader: mockPauseLoader, credentialProvider: aws_credentials.NewCredentials(mockCredentialsProvider), mobyPlugins: mockMobyPlugins, } @@ -1064,7 +1143,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 +1168,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..3b197e5f60f 100644 --- a/agent/app/agent_unix.go +++ b/agent/app/agent_unix.go @@ -25,7 +25,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,17 +85,6 @@ 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 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. - // Return the error as terminal for these cases - return err, true - } - return err, false - } - if err := agent.startUdevWatcher(state, taskEngine.StateChangeEvents()); err != nil { // If udev watcher was not initialized in this run because of the udev socket // file not being available etc, the Agent might be able to retry and succeed @@ -251,3 +239,10 @@ func (agent *ecsAgent) getPlatformDevices() []*ecs.PlatformDevice { } return nil } + +func (agent *ecsAgent) loadPauseContainer() error { + // 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..3a479557a46 100644 --- a/agent/app/agent_unix_test.go +++ b/agent/app/agent_unix_test.go @@ -18,8 +18,6 @@ package app import ( "context" "errors" - "fmt" - "reflect" "sync" "testing" @@ -34,7 +32,6 @@ import ( "github.com/aws/amazon-ecs-agent/agent/engine" mock_dockerstate "github.com/aws/amazon-ecs-agent/agent/engine/dockerstate/mocks" mock_engine "github.com/aws/amazon-ecs-agent/agent/engine/mocks" - "github.com/aws/amazon-ecs-agent/agent/eni/pause" mock_pause "github.com/aws/amazon-ecs-agent/agent/eni/pause/mocks" "github.com/aws/amazon-ecs-agent/agent/eventstream" mock_gpu "github.com/aws/amazon-ecs-agent/agent/gpu/mocks" @@ -66,6 +63,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 +85,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 +112,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, @@ -171,6 +172,8 @@ func TestDoStartTaskENIHappyPath(t *testing.T) { client.EXPECT().DiscoverTelemetryEndpoint(gomock.Any()).Return( "tele-endpoint", nil).AnyTimes() mockMetadata.EXPECT().OutpostARN().Return("", nil) + mockPauseLoader.EXPECT().LoadImage(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() + mockPauseLoader.EXPECT().IsLoaded(gomock.Any()).Return(true, nil).AnyTimes() gomock.InOrder( mockOS.EXPECT().Getpid().Return(10), @@ -182,7 +185,6 @@ 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), state.EXPECT().ENIByMac(gomock.Any()).Return(nil, false).AnyTimes(), mockCredentialsProvider.EXPECT().Retrieve().Return(credentials.Value{}, nil), dockerClient.EXPECT().SupportedVersions().Return(nil), @@ -470,48 +472,6 @@ func TestInitializeTaskENIDependenciesQueryCNICapabilitiesError(t *testing.T) { assert.True(t, ok) } -func TestInitializeTaskENIDependenciesPauseLoaderError(t *testing.T) { - errorsToIsTerminal := map[error]bool{ - errors.New("error"): false, - pause.NewNoSuchFileError(errors.New("error")): true, - pause.NewUnsupportedPlatformError(errors.New("error")): true, - } - for loadErr, expectedIsTerminal := range errorsToIsTerminal { - errType := reflect.TypeOf(loadErr) - t.Run(fmt.Sprintf("error type %s->expected exit code %t", errType, expectedIsTerminal), func(t *testing.T) { - ctrl, state, taskEngine, mockOS := setupMocksForInitializeTaskENIDependencies(t) - defer ctrl.Finish() - - mockMetadata := mock_ec2.NewMockEC2MetadataClient(ctrl) - cniClient := mock_ecscni.NewMockCNIClient(ctrl) - mockPauseLoader := mock_pause.NewMockLoader(ctrl) - cniCapabilities := []string{ecscni.CapabilityAWSVPCNetworkingMode} - gomock.InOrder( - mockOS.EXPECT().Getpid().Return(10), - mockMetadata.EXPECT().PrimaryENIMAC().Return(mac, nil), - mockMetadata.EXPECT().VPCID(mac).Return(vpcID, nil), - mockMetadata.EXPECT().SubnetID(mac).Return(subnetID, nil), - cniClient.EXPECT().Capabilities(ecscni.ECSENIPluginName).Return(cniCapabilities, nil), - 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), - ) - cfg := getTestConfig() - agent := &ecsAgent{ - os: mockOS, - ec2MetadataClient: mockMetadata, - cniClient: cniClient, - pauseLoader: mockPauseLoader, - cfg: &cfg, - } - err, ok := agent.initializeTaskENIDependencies(state, taskEngine) - assert.Error(t, err) - assert.Equal(t, expectedIsTerminal, ok) - }) - } -} - // TODO: At some point in the future, enisetup.New() will be refactored to be // platform independent and we would be able to wrap it in a factory interface // so that we can mock the factory and test the initialization code path for @@ -524,6 +484,7 @@ func TestDoStartCgroupInitHappyPath(t *testing.T) { mockCredentialsProvider := app_mocks.NewMockProvider(ctrl) mockControl := mock_control.NewMockControl(ctrl) mockMobyPlugins := mock_mobypkgwrapper.NewMockPlugins(ctrl) + mockPauseLoader := mock_pause.NewMockLoader(ctrl) var discoverEndpointsInvoked sync.WaitGroup discoverEndpointsInvoked.Add(2) containerChangeEvents := make(chan dockerapi.DockerContainerChangeEvent) @@ -534,6 +495,8 @@ func TestDoStartCgroupInitHappyPath(t *testing.T) { imageManager.EXPECT().StartImageCleanupProcess(gomock.Any()).MaxTimes(1) mockCredentialsProvider.EXPECT().IsExpired().Return(false).AnyTimes() ec2MetadataClient.EXPECT().OutpostARN().Return("", nil) + mockPauseLoader.EXPECT().LoadImage(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() + mockPauseLoader.EXPECT().IsLoaded(gomock.Any()).Return(true, nil).AnyTimes() gomock.InOrder( mockControl.EXPECT().Init().Return(nil), @@ -572,6 +535,7 @@ func TestDoStartCgroupInitHappyPath(t *testing.T) { ctx: ctx, cfg: &cfg, credentialProvider: credentials.NewCredentials(mockCredentialsProvider), + pauseLoader: mockPauseLoader, dockerClient: dockerClient, terminationHandler: func(saver statemanager.Saver, taskEngine engine.TaskEngine) {}, mobyPlugins: mockMobyPlugins, @@ -605,6 +569,7 @@ func TestDoStartCgroupInitErrorPath(t *testing.T) { mockCredentialsProvider := app_mocks.NewMockProvider(ctrl) mockControl := mock_control.NewMockControl(ctrl) + mockPauseLoader := mock_pause.NewMockLoader(ctrl) var discoverEndpointsInvoked sync.WaitGroup discoverEndpointsInvoked.Add(2) @@ -612,6 +577,8 @@ func TestDoStartCgroupInitErrorPath(t *testing.T) { dockerClient.EXPECT().SupportedVersions().Return(apiVersions) imageManager.EXPECT().StartImageCleanupProcess(gomock.Any()).MaxTimes(1) mockCredentialsProvider.EXPECT().IsExpired().Return(false).AnyTimes() + mockPauseLoader.EXPECT().LoadImage(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() + mockPauseLoader.EXPECT().IsLoaded(gomock.Any()).Return(true, nil).AnyTimes() mockControl.EXPECT().Init().Return(errors.New("test error")) @@ -626,6 +593,7 @@ func TestDoStartCgroupInitErrorPath(t *testing.T) { cfg: &cfg, credentialProvider: credentials.NewCredentials(mockCredentialsProvider), dockerClient: dockerClient, + pauseLoader: mockPauseLoader, terminationHandler: func(saver statemanager.Saver, taskEngine engine.TaskEngine) {}, resourceFields: &taskresource.ResourceFields{ Control: mockControl, @@ -646,6 +614,7 @@ func TestDoStartGPUManagerHappyPath(t *testing.T) { mockGPUManager := mock_gpu.NewMockGPUManager(ctrl) mockMobyPlugins := mock_mobypkgwrapper.NewMockPlugins(ctrl) ec2MetadataClient := mock_ec2.NewMockEC2MetadataClient(ctrl) + mockPauseLoader := mock_pause.NewMockLoader(ctrl) devices := []*ecs.PlatformDevice{ { @@ -670,6 +639,8 @@ func TestDoStartGPUManagerHappyPath(t *testing.T) { imageManager.EXPECT().StartImageCleanupProcess(gomock.Any()).MaxTimes(1) mockCredentialsProvider.EXPECT().IsExpired().Return(false).AnyTimes() ec2MetadataClient.EXPECT().OutpostARN().Return("", nil) + mockPauseLoader.EXPECT().LoadImage(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() + mockPauseLoader.EXPECT().IsLoaded(gomock.Any()).Return(true, nil).AnyTimes() gomock.InOrder( mockGPUManager.EXPECT().Initialize().Return(nil), @@ -712,6 +683,7 @@ func TestDoStartGPUManagerHappyPath(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, @@ -744,6 +716,7 @@ func TestDoStartGPUManagerInitError(t *testing.T) { mockCredentialsProvider := app_mocks.NewMockProvider(ctrl) mockGPUManager := mock_gpu.NewMockGPUManager(ctrl) + mockPauseLoader := mock_pause.NewMockLoader(ctrl) var discoverEndpointsInvoked sync.WaitGroup discoverEndpointsInvoked.Add(2) @@ -752,6 +725,8 @@ func TestDoStartGPUManagerInitError(t *testing.T) { imageManager.EXPECT().StartImageCleanupProcess(gomock.Any()).MaxTimes(1) mockCredentialsProvider.EXPECT().IsExpired().Return(false).AnyTimes() mockGPUManager.EXPECT().Initialize().Return(errors.New("init error")) + mockPauseLoader.EXPECT().LoadImage(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() + mockPauseLoader.EXPECT().IsLoaded(gomock.Any()).Return(true, nil).AnyTimes() cfg := getTestConfig() cfg.GPUSupportEnabled = true @@ -763,6 +738,7 @@ func TestDoStartGPUManagerInitError(t *testing.T) { cfg: &cfg, credentialProvider: credentials.NewCredentials(mockCredentialsProvider), dockerClient: dockerClient, + pauseLoader: mockPauseLoader, terminationHandler: func(saver statemanager.Saver, taskEngine engine.TaskEngine) {}, resourceFields: &taskresource.ResourceFields{ NvidiaGPUManager: mockGPUManager, @@ -774,3 +750,51 @@ func TestDoStartGPUManagerInitError(t *testing.T) { assert.Equal(t, exitcodes.ExitError, status) } + +func TestDoStartTaskENIPauseError(t *testing.T) { + ctrl, credentialsManager, state, imageManager, client, + dockerClient, _, _ := setup(t) + defer ctrl.Finish() + + cniClient := mock_ecscni.NewMockCNIClient(ctrl) + mockCredentialsProvider := app_mocks.NewMockProvider(ctrl) + mockPauseLoader := mock_pause.NewMockLoader(ctrl) + mockOS := mock_oswrapper.NewMockOS(ctrl) + mockMetadata := mock_ec2.NewMockEC2MetadataClient(ctrl) + mockMobyPlugins := mock_mobypkgwrapper.NewMockPlugins(ctrl) + + var discoverEndpointsInvoked sync.WaitGroup + discoverEndpointsInvoked.Add(2) + + // These calls are expected to happen, but cannot be ordered as they are + // invoked via go routines, which will lead to occasional test failures + mockCredentialsProvider.EXPECT().IsExpired().Return(false).AnyTimes() + dockerClient.EXPECT().Version(gomock.Any(), gomock.Any()).AnyTimes() + dockerClient.EXPECT().SupportedVersions().Return(apiVersions) + dockerClient.EXPECT().ListContainers(gomock.Any(), gomock.Any(), gomock.Any()).Return( + dockerapi.ListContainersResponse{}).AnyTimes() + imageManager.EXPECT().StartImageCleanupProcess(gomock.Any()).MaxTimes(1) + mockPauseLoader.EXPECT().LoadImage(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, errors.New("error")).AnyTimes() + + cfg := getTestConfig() + cfg.TaskENIEnabled = true + cfg.ENITrunkingEnabled = true + ctx, _ := context.WithCancel(context.TODO()) + agent := &ecsAgent{ + ctx: ctx, + cfg: &cfg, + credentialProvider: credentials.NewCredentials(mockCredentialsProvider), + dockerClient: dockerClient, + pauseLoader: mockPauseLoader, + cniClient: cniClient, + os: mockOS, + ec2MetadataClient: mockMetadata, + terminationHandler: func(saver statemanager.Saver, taskEngine engine.TaskEngine) {}, + mobyPlugins: mockMobyPlugins, + } + + status := agent.doStart(eventstream.NewEventStream("events", ctx), + credentialsManager, state, imageManager, client) + + assert.Equal(t, exitcodes.ExitError, status) +} 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..74eeeabab3d 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 +} 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..a1e5ef1ea05 100644 --- a/agent/eni/pause/mocks/load_mocks.go +++ b/agent/eni/pause/mocks/load_mocks.go @@ -52,16 +52,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) } 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..c68561cff7d 100644 --- a/agent/eni/pause/pause_linux_test.go +++ b/agent/eni/pause/pause_linux_test.go @@ -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)) +}