Skip to content

Commit

Permalink
Awsvpc windows : Check if the pause image is loaded on the container …
Browse files Browse the repository at this point in the history
…instance (aws#2498)

* Changes to check if the pause image has been loaded on agent startup. We will cache pause image on ECS-Optimized Windows AMI. On agent startup, we check if the pause image is already loaded.  The following changes are made as part of this commit:

1. Moved common functions from pause_linux.go to load.go
2. Moved the common code from IsLoaded function to a function in load.go
3. Unit tests for the same were moved from Linux specific files to common files
  • Loading branch information
rawahars authored and Harsh Rawat committed Jun 24, 2021
1 parent 26d78f6 commit b8df8a5
Show file tree
Hide file tree
Showing 11 changed files with 260 additions and 156 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ The following targets are available. Each may be run with `make <target>`.

### Standalone (on Windows)

The Amazon ECS Container Agent may be built by typing `go build -o amazon-ecs-agent.exe ./agent`.
The Amazon ECS Container Agent may be built by invoking `scripts\build_agent.ps1`

### Scripts (on Windows)

Expand Down
6 changes: 5 additions & 1 deletion agent/app/agent_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -283,5 +283,9 @@ func (agent *ecsAgent) getPlatformDevices() []*ecs.PlatformDevice {
}

func (agent *ecsAgent) loadPauseContainer() error {
return nil
// The pause image would be cached in th ECS-Optimized Windows AMI's and will be available. We will throw an error if the image is not loaded.
// If the agent is run on non-supported instances then pause image has to be loaded manually by the client.
_, err := agent.pauseLoader.IsLoaded(agent.dockerClient)

return err
}
2 changes: 2 additions & 0 deletions agent/config/config_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@ func DefaultConfig() Config {
PollingMetricsWaitDuration: DefaultPollingMetricsWaitDuration,
GMSACapable: true,
FSxWindowsFileServerCapable: true,
PauseContainerImageName: DefaultPauseContainerImageName,
PauseContainerTag: DefaultPauseContainerTag,
}
}

Expand Down
33 changes: 33 additions & 0 deletions agent/eni/pause/load.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,13 @@ package pause

import (
"context"
"fmt"

"github.com/aws/amazon-ecs-agent/agent/config"
"github.com/aws/amazon-ecs-agent/agent/dockerclient/dockerapi"
log "github.com/cihub/seelog"
"github.com/docker/docker/api/types"
"github.com/pkg/errors"
)

// Loader defines an interface for loading the pause container image. This is mostly
Expand All @@ -34,3 +37,33 @@ type loader struct{}
func New() Loader {
return &loader{}
}

// This function uses the DockerClient to inspect the image with the given name and tag.
func getPauseContainerImage(name string, tag string, dockerClient dockerapi.DockerClient) (*types.ImageInspect, error) {
imageName := fmt.Sprintf("%s:%s", name, tag)
log.Debugf("Inspecting pause container image: %s", imageName)

image, err := dockerClient.InspectImage(imageName)
if err != nil {
return nil, errors.Wrapf(err,
"pause container load: failed to inspect image: %s", imageName)
}

return image, nil
}

// Common function for linux and windows to check if the container pause image has been loaded
func isImageLoaded(dockerClient dockerapi.DockerClient) (bool, error) {
image, err := getPauseContainerImage(
config.DefaultPauseContainerImageName, config.DefaultPauseContainerTag, dockerClient)

if err != nil {
return false, err
}

if image == nil || image.ID == "" {
return false, nil
}

return true, nil
}
148 changes: 148 additions & 0 deletions agent/eni/pause/load_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
// +build unit

// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may
// not use this file except in compliance with the License. A copy of the
// License is located at
//
// http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file is distributed
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
// express or implied. See the License for the specific language governing
// permissions and limitations under the License.

package pause

import (
"context"
"errors"
"testing"

"github.com/aws/amazon-ecs-agent/agent/config"
"github.com/aws/amazon-ecs-agent/agent/dockerclient/dockerapi"
mock_sdkclient "github.com/aws/amazon-ecs-agent/agent/dockerclient/sdkclient/mocks"
mock_sdkclientfactory "github.com/aws/amazon-ecs-agent/agent/dockerclient/sdkclientfactory/mocks"

"github.com/docker/docker/api/types"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
)

const (
pauseName = "pause"
pauseTag = "tag"
)

var defaultConfig = config.DefaultConfig()

func TestGetPauseContainerImageInspectImageError(t *testing.T) {
ctrl := gomock.NewController(t)
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(), pauseName+":"+pauseTag).Return(
types.ImageInspect{}, nil, errors.New("error"))

_, err = getPauseContainerImage(pauseName, pauseTag, client)
assert.Error(t, err)
}

func TestGetPauseContainerHappyPath(t *testing.T) {
ctrl := gomock.NewController(t)
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(), pauseName+":"+pauseTag).Return(types.ImageInspect{}, nil, nil)

_, err = getPauseContainerImage(pauseName, pauseTag, client)
assert.NoError(t, err)
}

func TestIsImageLoadedHappyPath(t *testing.T) {
ctrl := gomock.NewController(t)
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 := isImageLoaded(client)
assert.NoError(t, err)
assert.True(t, isLoaded)
}

func TestIsImageLoadedNotLoaded(t *testing.T) {
ctrl := gomock.NewController(t)
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 := isImageLoaded(client)
assert.NoError(t, err)
assert.False(t, isLoaded)
}

func TestIsImageLoadedError(t *testing.T) {
ctrl := gomock.NewController(t)
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 := isImageLoaded(client)
assert.Error(t, err)
assert.False(t, isLoaded)
}
28 changes: 1 addition & 27 deletions agent/eni/pause/pause_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ package pause

import (
"context"
"fmt"
"os"

"github.com/aws/amazon-ecs-agent/agent/config"
Expand All @@ -41,19 +40,7 @@ func (*loader) LoadImage(ctx context.Context, cfg *config.Config, dockerClient d
}

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
return isImageLoaded(dockerClient)
}

var open = os.Open
Expand All @@ -76,16 +63,3 @@ func loadFromFile(ctx context.Context, path string, dockerClient dockerapi.Docke
return nil

}

func getPauseContainerImage(name string, tag string, dockerClient dockerapi.DockerClient) (*types.ImageInspect, error) {
imageName := fmt.Sprintf("%s:%s", name, tag)
log.Debugf("Inspecting pause container image: %s", imageName)

image, err := dockerClient.InspectImage(imageName)
if err != nil {
return nil, errors.Wrapf(err,
"pause container load: failed to inspect image: %s", imageName)
}

return image, nil
}
Loading

0 comments on commit b8df8a5

Please sign in to comment.