Skip to content

Commit

Permalink
az expose to ecs metadatafile
Browse files Browse the repository at this point in the history
  • Loading branch information
cyastella committed Nov 13, 2018
1 parent 44c6b76 commit 0b8f1e4
Show file tree
Hide file tree
Showing 8 changed files with 127 additions and 0 deletions.
1 change: 1 addition & 0 deletions agent/app/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,7 @@ func (agent *ecsAgent) doStart(containerChangeEventStream *eventstream.EventStre
// Add container instance ARN to metadata manager
if agent.cfg.ContainerMetadataEnabled {
agent.metadataManager.SetContainerInstanceARN(agent.containerInstanceARN)
agent.metadataManager.SetAvailabilityZone(agent.availabilityZone)
}

// Begin listening to the docker daemon and saving changes
Expand Down
70 changes: 70 additions & 0 deletions agent/app/agent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,22 @@ import (
"errors"
"fmt"
"sort"
"sync"
"testing"

apierrors "github.com/aws/amazon-ecs-agent/agent/api/errors"
"github.com/aws/amazon-ecs-agent/agent/api/mocks"
"github.com/aws/amazon-ecs-agent/agent/app/factory/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/containermetadata/mocks"
"github.com/aws/amazon-ecs-agent/agent/credentials/mocks"
"github.com/aws/amazon-ecs-agent/agent/dockerclient"
"github.com/aws/amazon-ecs-agent/agent/dockerclient/dockerapi"
"github.com/aws/amazon-ecs-agent/agent/dockerclient/dockerapi/mocks"
"github.com/aws/amazon-ecs-agent/agent/ec2/mocks"
"github.com/aws/amazon-ecs-agent/agent/ecs_client/model/ecs"
"github.com/aws/amazon-ecs-agent/agent/engine"
"github.com/aws/amazon-ecs-agent/agent/engine/dockerstate/mocks"
"github.com/aws/amazon-ecs-agent/agent/engine/mocks"
"github.com/aws/amazon-ecs-agent/agent/eventstream"
Expand Down Expand Up @@ -293,6 +297,72 @@ func TestDoStartRegisterContainerInstanceErrorNonTerminal(t *testing.T) {
assert.Equal(t, exitcodes.ExitError, exitCode)
}

func TestDoStartRegisterAvailabilityZone(t *testing.T) {
ctrl, credentialsManager, state, imageManager, client,
dockerClient, _, _ := setup(t)
defer ctrl.Finish()

var discoverEndpointsInvoked sync.WaitGroup
discoverEndpointsInvoked.Add(2)
mockMobyPlugins := mock_mobypkgwrapper.NewMockPlugins(ctrl)
dockerClient.EXPECT().Version(gomock.Any(), gomock.Any()).AnyTimes()
mockCredentialsProvider := app_mocks.NewMockProvider(ctrl)
containermetadata := mock_containermetadata.NewMockManager(ctrl)
imageManager.EXPECT().StartImageCleanupProcess(gomock.Any()).MaxTimes(1)
dockerClient.EXPECT().ListContainers(gomock.Any(), gomock.Any(), gomock.Any()).Return(
dockerapi.ListContainersResponse{}).AnyTimes()
client.EXPECT().DiscoverPollEndpoint(gomock.Any()).Do(func(x interface{}) {
// Ensures that the test waits until acs session has bee started
discoverEndpointsInvoked.Done()
}).Return("poll-endpoint", nil)
client.EXPECT().DiscoverPollEndpoint(gomock.Any()).Return("acs-endpoint", nil).AnyTimes()
client.EXPECT().DiscoverTelemetryEndpoint(gomock.Any()).Do(func(x interface{}) {
// Ensures that the test waits until telemetry session has bee started
discoverEndpointsInvoked.Done()
}).Return("telemetry-endpoint", nil)
client.EXPECT().DiscoverTelemetryEndpoint(gomock.Any()).Return(
"tele-endpoint", nil).AnyTimes()

gomock.InOrder(
dockerClient.EXPECT().SupportedVersions().Return(apiVersions),
mockCredentialsProvider.EXPECT().Retrieve().Return(aws_credentials.Value{}, nil),
dockerClient.EXPECT().SupportedVersions().Return(nil),
dockerClient.EXPECT().KnownVersions().Return(nil),
mockMobyPlugins.EXPECT().Scan().AnyTimes().Return([]string{""}, nil),
dockerClient.EXPECT().ListPluginsWithFilters(gomock.Any(), gomock.Any(), gomock.Any(),
gomock.Any()).AnyTimes().Return([]string{}, nil),
client.EXPECT().RegisterContainerInstance(gomock.Any(), gomock.Any(), gomock.Any()).Return(
"arn:123", availabilityZone, nil),
containermetadata.EXPECT().SetContainerInstanceARN("arn:123"),
containermetadata.EXPECT().SetAvailabilityZone(availabilityZone),
imageManager.EXPECT().SetSaver(gomock.Any()),
dockerClient.EXPECT().ContainerEvents(gomock.Any()),
state.EXPECT().AllImageStates().Return(nil),
state.EXPECT().AllTasks().Return(nil),
)

cfg := getTestConfig()
cfg.ContainerMetadataEnabled = true
ctx, cancel := context.WithCancel(context.TODO())

// Cancel the context to cancel async routines
defer cancel()
agent := &ecsAgent{
ctx: ctx,
cfg: &cfg,
dockerClient: dockerClient,
credentialProvider: aws_credentials.NewCredentials(mockCredentialsProvider),
mobyPlugins: mockMobyPlugins,
metadataManager: containermetadata,
terminationHandler: func(saver statemanager.Saver, taskEngine engine.TaskEngine) {},
}

go agent.doStart(eventstream.NewEventStream("events", ctx),
credentialsManager, state, imageManager, client)

discoverEndpointsInvoked.Wait()
}

func TestNewTaskEngineRestoreFromCheckpointNoEC2InstanceIDToLoadHappyPath(t *testing.T) {
ctrl, credentialsManager, state, imageManager, _,
dockerClient, stateManagerFactory, saveableOptionFactory := setup(t)
Expand Down
9 changes: 9 additions & 0 deletions agent/containermetadata/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ const (
// operations
type Manager interface {
SetContainerInstanceARN(string)
SetAvailabilityZone(string)
Create(*docker.Config, *docker.HostConfig, *apitask.Task, string) error
Update(context.Context, string, *apitask.Task, string) error
Clean(string) error
Expand All @@ -65,6 +66,8 @@ type metadataManager struct {
osWrap oswrapper.OS
// ioutilWrap is a wrapper for 'ioutil' package operations
ioutilWrap ioutilwrapper.IOUtil
// availabilityZone is the availabiltyZone where task is in
availabilityZone string
}

// NewManager creates a metadataManager for a given DockerTaskEngine settings.
Expand All @@ -85,6 +88,12 @@ func (manager *metadataManager) SetContainerInstanceARN(containerInstanceARN str
manager.containerInstanceARN = containerInstanceARN
}

// SetAvailabilityzone sets the metadataManager's AvailabilityZone which is not available
// at its creation as this information is not present immediately at the agent's startup
func (manager *metadataManager) SetAvailabilityZone(availabilityZone string) {
manager.availabilityZone = availabilityZone
}

// Create creates the metadata file and adds the metadata directory to
// the container's mounted host volumes
// Pointer hostConfig is modified directly so there is risk of concurrency errors.
Expand Down
11 changes: 11 additions & 0 deletions agent/containermetadata/manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ const (
taskDefinitionRevision = "8"
containerName = "container"
dataDir = "ecs_mockdata"
availabilityZone = "us-west-2b"
)

func managerSetup(t *testing.T) (*mock_containermetadata.MockDockerMetadataClient, *mock_ioutilwrapper.MockIOUtil, *mock_oswrapper.MockOS, *mock_oswrapper.MockFile, func()) {
Expand All @@ -62,6 +63,16 @@ func TestSetContainerInstanceARN(t *testing.T) {
assert.Equal(t, mockARN, newManager.containerInstanceARN)
}

// TestAvailabilityZone checks whether the container availabilityZone is set correctly.
func TestSetAvailabilityZone(t *testing.T) {
_, _, _, _, done := managerSetup(t)
defer done()
mockAvailabilityZone := availabilityZone
newManager := &metadataManager{}
newManager.SetAvailabilityZone(mockAvailabilityZone)
assert.Equal(t, mockAvailabilityZone, newManager.availabilityZone)
}

// TestCreateMalformedFilepath checks case when taskARN is invalid resulting in an invalid file path
func TestCreateMalformedFilepath(t *testing.T) {
_, _, _, _, done := managerSetup(t)
Expand Down
10 changes: 10 additions & 0 deletions agent/containermetadata/mocks/containermetadata_mocks.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions agent/containermetadata/parse_metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ func (manager *metadataManager) parseMetadataAtContainerCreate(task *apitask.Tas
},
containerInstanceARN: manager.containerInstanceARN,
metadataStatus: MetadataInitial,
availabilityZone: manager.availabilityZone,
}
}

Expand All @@ -59,6 +60,7 @@ func (manager *metadataManager) parseMetadata(dockerContainer *docker.Container,
dockerContainerMetadata: dockerMD,
containerInstanceARN: manager.containerInstanceARN,
metadataStatus: MetadataReady,
availabilityZone: manager.availabilityZone,
}
}

Expand Down
21 changes: 21 additions & 0 deletions agent/containermetadata/parse_metadata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,19 +37,22 @@ func TestParseContainerCreate(t *testing.T) {
mockContainerName := containerName
mockCluster := cluster
mockContainerInstanceARN := containerInstanceARN
mockAvailabilityZone := availabilityZone

expectedStatus := string(MetadataInitial)

newManager := &metadataManager{
cluster: mockCluster,
containerInstanceARN: mockContainerInstanceARN,
availabilityZone: mockAvailabilityZone,
}

metadata := newManager.parseMetadataAtContainerCreate(mockTask, mockContainerName)
assert.Equal(t, metadata.cluster, mockCluster, "Expected cluster "+mockCluster)
assert.Equal(t, metadata.taskMetadata.containerName, mockContainerName, "Expected container name "+mockContainerName)
assert.Equal(t, metadata.taskMetadata.taskARN, mockTaskARN, "Expected task ARN "+mockTaskARN)
assert.Equal(t, metadata.containerInstanceARN, mockContainerInstanceARN, "Expected container instance ARN "+mockContainerInstanceARN)
assert.Equal(t, metadata.availabilityZone, mockAvailabilityZone, "Expected availabilityZone "+mockAvailabilityZone)
assert.Equal(t, metadata.taskMetadata.taskDefinitionFamily, mockTaskDefinitionFamily, "Expected task definition family "+mockTaskDefinitionFamily)
assert.Equal(t, metadata.taskMetadata.taskDefinitionRevision, mockTaskDefinitionRevision, "Expected task definition revision "+mockTaskDefinitionRevision)
assert.Equal(t, string(metadata.metadataStatus), expectedStatus, "Expected status "+expectedStatus)
Expand All @@ -61,19 +64,22 @@ func TestParseHasNoContainer(t *testing.T) {
mockContainerName := containerName
mockCluster := cluster
mockContainerInstanceARN := containerInstanceARN
mockAvailabilityZone := availabilityZone

expectedStatus := string(MetadataReady)

newManager := &metadataManager{
cluster: mockCluster,
containerInstanceARN: mockContainerInstanceARN,
availabilityZone: mockAvailabilityZone,
}

metadata := newManager.parseMetadata(nil, mockTask, mockContainerName)
assert.Equal(t, metadata.cluster, mockCluster, "Expected cluster "+mockCluster)
assert.Equal(t, metadata.taskMetadata.containerName, mockContainerName, "Expected container name "+mockContainerName)
assert.Equal(t, metadata.taskMetadata.taskARN, mockTaskARN, "Expected task ARN "+mockTaskARN)
assert.Equal(t, metadata.containerInstanceARN, mockContainerInstanceARN, "Expected container instance ARN "+mockContainerInstanceARN)
assert.Equal(t, metadata.availabilityZone, mockAvailabilityZone, "Expected availabilityZone "+mockAvailabilityZone)
assert.Equal(t, string(metadata.metadataStatus), expectedStatus, "Expected status "+expectedStatus)
assert.Equal(t, metadata.dockerContainerMetadata.containerID, "", "Expected empty container metadata")
assert.Equal(t, metadata.dockerContainerMetadata.dockerContainerName, "", "Expected empty container metadata")
Expand All @@ -87,6 +93,7 @@ func TestParseHasConfig(t *testing.T) {
mockContainerName := containerName
mockCluster := cluster
mockContainerInstanceARN := containerInstanceARN
mockAvailabilityZone := availabilityZone

mockConfig := &docker.Config{Image: "image"}

Expand All @@ -100,6 +107,7 @@ func TestParseHasConfig(t *testing.T) {
newManager := &metadataManager{
cluster: mockCluster,
containerInstanceARN: mockContainerInstanceARN,
availabilityZone: mockAvailabilityZone,
}

metadata := newManager.parseMetadata(mockContainer, mockTask, mockContainerName)
Expand All @@ -108,6 +116,7 @@ func TestParseHasConfig(t *testing.T) {
assert.Equal(t, metadata.taskMetadata.containerName, mockContainerName, "Expected container name "+mockContainerName)
assert.Equal(t, metadata.taskMetadata.taskARN, mockTaskARN, "Expected task ARN "+mockTaskARN)
assert.Equal(t, metadata.containerInstanceARN, mockContainerInstanceARN, "Expected container instance ARN "+mockContainerInstanceARN)
assert.Equal(t, metadata.availabilityZone, mockAvailabilityZone, "Expected availabilityZone "+mockAvailabilityZone)
assert.Equal(t, string(metadata.metadataStatus), expectedStatus, "Expected status "+expectedStatus)
assert.Equal(t, metadata.dockerContainerMetadata.imageName, "image", "Expected nonempty imageID")
}
Expand All @@ -118,6 +127,7 @@ func TestParseHasNetworkSettingsPortBindings(t *testing.T) {
mockContainerName := containerName
mockCluster := cluster
mockContainerInstanceARN := containerInstanceARN
mockAvailabilityZone := availabilityZone

mockPorts := make(map[docker.Port][]docker.PortBinding)
mockPortBinding := make([]docker.PortBinding, 0)
Expand All @@ -136,13 +146,15 @@ func TestParseHasNetworkSettingsPortBindings(t *testing.T) {
newManager := &metadataManager{
cluster: mockCluster,
containerInstanceARN: mockContainerInstanceARN,
availabilityZone: mockAvailabilityZone,
}

metadata := newManager.parseMetadata(mockContainer, mockTask, mockContainerName)
assert.Equal(t, metadata.cluster, mockCluster, "Expected cluster "+mockCluster)
assert.Equal(t, metadata.taskMetadata.containerName, mockContainerName, "Expected container name "+mockContainerName)
assert.Equal(t, metadata.taskMetadata.taskARN, mockTaskARN, "Expected task ARN "+mockTaskARN)
assert.Equal(t, metadata.containerInstanceARN, mockContainerInstanceARN, "Expected container instance ARN "+mockContainerInstanceARN)
assert.Equal(t, metadata.availabilityZone, mockAvailabilityZone, "Expected availabilityZone "+mockAvailabilityZone)
assert.Equal(t, string(metadata.metadataStatus), expectedStatus, "Expected status "+expectedStatus)
assert.Equal(t, len(metadata.dockerContainerMetadata.networkInfo.networks), 2, "Expected two networks")

Expand All @@ -158,6 +170,7 @@ func TestParseHasNetworkSettingsNetworksEmpty(t *testing.T) {
mockContainerName := containerName
mockCluster := cluster
mockContainerInstanceARN := containerInstanceARN
mockAvailabilityZone := availabilityZone

mockHostConfig := &docker.HostConfig{NetworkMode: "bridge"}
mockNetworkSettings := &docker.NetworkSettings{IPAddress: "0.0.0.0"}
Expand All @@ -168,13 +181,15 @@ func TestParseHasNetworkSettingsNetworksEmpty(t *testing.T) {
newManager := &metadataManager{
cluster: mockCluster,
containerInstanceARN: mockContainerInstanceARN,
availabilityZone: mockAvailabilityZone,
}

metadata := newManager.parseMetadata(mockContainer, mockTask, mockContainerName)
assert.Equal(t, metadata.cluster, mockCluster, "Expected cluster "+mockCluster)
assert.Equal(t, metadata.taskMetadata.containerName, mockContainerName, "Expected container name "+mockContainerName)
assert.Equal(t, metadata.taskMetadata.taskARN, mockTaskARN, "Expected task ARN "+mockTaskARN)
assert.Equal(t, metadata.containerInstanceARN, mockContainerInstanceARN, "Expected container instance ARN "+mockContainerInstanceARN)
assert.Equal(t, metadata.availabilityZone, mockAvailabilityZone, "Expected availabilityZone "+mockAvailabilityZone)
assert.Equal(t, string(metadata.metadataStatus), expectedStatus, "Expected status "+expectedStatus)
assert.Equal(t, len(metadata.dockerContainerMetadata.networkInfo.networks), 1, "Expected one network")
}
Expand All @@ -185,6 +200,7 @@ func TestParseHasNetworkSettingsNetworksNonEmpty(t *testing.T) {
mockContainerName := containerName
mockCluster := cluster
mockContainerInstanceARN := containerInstanceARN
mockAvailabilityZone := availabilityZone

mockHostConfig := &docker.HostConfig{NetworkMode: "bridge"}
mockNetworks := make(map[string]docker.ContainerNetwork)
Expand All @@ -198,13 +214,15 @@ func TestParseHasNetworkSettingsNetworksNonEmpty(t *testing.T) {
newManager := &metadataManager{
cluster: mockCluster,
containerInstanceARN: mockContainerInstanceARN,
availabilityZone: mockAvailabilityZone,
}

metadata := newManager.parseMetadata(mockContainer, mockTask, mockContainerName)
assert.Equal(t, metadata.cluster, mockCluster, "Expected cluster "+mockCluster)
assert.Equal(t, metadata.taskMetadata.containerName, mockContainerName, "Expected container name "+mockContainerName)
assert.Equal(t, metadata.taskMetadata.taskARN, mockTaskARN, "Expected task ARN "+mockTaskARN)
assert.Equal(t, metadata.containerInstanceARN, mockContainerInstanceARN, "Expected container instance ARN "+mockContainerInstanceARN)
assert.Equal(t, metadata.availabilityZone, mockAvailabilityZone, "Expected AvailabilityZone"+mockAvailabilityZone)
assert.Equal(t, string(metadata.metadataStatus), expectedStatus, "Expected status "+expectedStatus)
assert.Equal(t, len(metadata.dockerContainerMetadata.networkInfo.networks), 2, "Expected two networks")
}
Expand All @@ -215,6 +233,7 @@ func TestParseTaskDefinitionSettings(t *testing.T) {
mockContainerName := containerName
mockCluster := cluster
mockContainerInstanceARN := containerInstanceARN
mockAvailabilityZone := availabilityZone

mockHostConfig := &docker.HostConfig{NetworkMode: "bridge"}
mockConfig := &docker.Config{Image: "image"}
Expand All @@ -226,6 +245,7 @@ func TestParseTaskDefinitionSettings(t *testing.T) {
newManager := &metadataManager{
cluster: mockCluster,
containerInstanceARN: mockContainerInstanceARN,
availabilityZone: mockAvailabilityZone,
}

metadata := newManager.parseMetadata(mockContainer, mockTask, mockContainerName)
Expand All @@ -235,6 +255,7 @@ func TestParseTaskDefinitionSettings(t *testing.T) {
assert.Equal(t, metadata.taskMetadata.taskDefinitionFamily, "", "Expected no task definition family")
assert.Equal(t, metadata.taskMetadata.taskDefinitionRevision, "", "Expected no task definition revision")
assert.Equal(t, metadata.containerInstanceARN, mockContainerInstanceARN, "Expected container instance ARN "+mockContainerInstanceARN)
assert.Equal(t, metadata.availabilityZone, mockAvailabilityZone, "Expected availabilityZone "+mockAvailabilityZone)
assert.Equal(t, string(metadata.metadataStatus), expectedStatus, "Expected status "+expectedStatus)

// now add the task definition details
Expand Down
3 changes: 3 additions & 0 deletions agent/containermetadata/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ type Metadata struct {
dockerContainerMetadata DockerContainerMetadata
containerInstanceARN string
metadataStatus MetadataStatus
availabilityZone string
}

// metadataSerializer is an intermediate struct that converts the information
Expand All @@ -148,6 +149,7 @@ type metadataSerializer struct {
Ports []apicontainer.PortBinding `json:"PortMappings,omitempty"`
Networks []Network `json:"Networks,omitempty"`
MetadataFileStatus MetadataStatus `json:"MetadataFileStatus,omitempty"`
AvailabilityZone string `json:"AvailabilityZone,omitempty"`
}

func (m Metadata) MarshalJSON() ([]byte, error) {
Expand All @@ -166,5 +168,6 @@ func (m Metadata) MarshalJSON() ([]byte, error) {
Ports: m.dockerContainerMetadata.ports,
Networks: m.dockerContainerMetadata.networkInfo.networks,
MetadataFileStatus: m.metadataStatus,
AvailabilityZone: m.availabilityZone,
})
}

0 comments on commit 0b8f1e4

Please sign in to comment.