From b208d502e4abee9890d82c5056d2e333638a77c5 Mon Sep 17 00:00:00 2001 From: Adnan Khan Date: Fri, 22 Sep 2017 09:44:45 -0700 Subject: [PATCH] functional_tests: added windows functional test --- agent/api/container.go | 3 ++ agent/config/config_windows.go | 3 +- agent/containermetadata/manager.go | 15 +++----- .../containermetadata/manager_windows_test.go | 5 +-- .../write_metadata_windows.go | 11 ++---- .../write_metadata_windows_test.go | 1 - agent/engine/docker_task_engine.go | 6 +-- .../task-definition.json | 12 ++++++ .../tests/functionaltests_windows_test.go | 37 +++++++++++++++++++ agent/functional_tests/util/utils_windows.go | 10 +++++ agent/statemanager/state_manager.go | 2 +- 11 files changed, 78 insertions(+), 27 deletions(-) create mode 100644 agent/functional_tests/testdata/taskdefinitions/mdservice-validator-windows/task-definition.json diff --git a/agent/api/container.go b/agent/api/container.go index 04db14d0473..f45d48c6e7b 100644 --- a/agent/api/container.go +++ b/agent/api/container.go @@ -311,6 +311,8 @@ func (c *Container) IsRunning() bool { return c.GetKnownStatus().IsRunning() } +// IsMetadataFileUpdated() returns true if the metadata file has been once the +// metadata file is ready and will no longer change func (c *Container) IsMetadataFileUpdated() bool { c.lock.RLock() defer c.lock.RUnlock() @@ -318,6 +320,7 @@ func (c *Container) IsMetadataFileUpdated() bool { return c.MetadataFileUpdated } +// SetMetadataFileUpdated sets the container's MetadataFileUpdated status to true func (c *Container) SetMetadataFileUpdated() { c.lock.Lock() defer c.lock.Unlock() diff --git a/agent/config/config_windows.go b/agent/config/config_windows.go index 21d8589afbd..c64d5dd0a15 100644 --- a/agent/config/config_windows.go +++ b/agent/config/config_windows.go @@ -61,7 +61,8 @@ func DefaultConfig() Config { }, ReservedPortsUDP: []uint16{}, DataDir: dataDir, - // DataDirOnHost is identical to DataDir for Windows because we do not run on a container + // DataDirOnHost is identical to DataDir for Windows because we do not + // run as a container DataDirOnHost: dataDir, // DisableMetrics is set to true on Windows as docker stats does not work DisableMetrics: true, diff --git a/agent/containermetadata/manager.go b/agent/containermetadata/manager.go index 2b5bb304588..5dcb543db81 100644 --- a/agent/containermetadata/manager.go +++ b/agent/containermetadata/manager.go @@ -31,6 +31,12 @@ const ( inspectContainerTimeout = 30 * time.Second metadataFile = "ecs-container-metadata.json" metadataPerm = 0644 + // MetadataInitial is the initial state of the metadata file which + // contains metadata provided by the ECS Agent + MetadataInitial = "INITIAL" + // MetadataReady is the final state of the metadata file which indicates + // it has acquired all the data it needs (Currently from the Agent and Docker) + MetadataReady = "READY" ) // MetadataStatus specifies the current update status of the metadata file. @@ -42,15 +48,6 @@ const ( // statuses should amended/appended accordingly. type MetadataStatus string -const ( - // MetadataInitial is the initial state of the metadata file which - // contains metadata provided by the ECS Agent - MetadataInitial = "INITIAL" - // MetadataReady is the final state of the metadata file which indicates - // it has acquired all the data it needs (Currently from the Agent and Docker) - MetadataReady = "READY" -) - // Manager is an interface that allows us to abstract away the metadata // operations type Manager interface { diff --git a/agent/containermetadata/manager_windows_test.go b/agent/containermetadata/manager_windows_test.go index 6121e6804a1..b5c3f369335 100644 --- a/agent/containermetadata/manager_windows_test.go +++ b/agent/containermetadata/manager_windows_test.go @@ -15,7 +15,6 @@ package containermetadata import ( - "errors" "testing" docker "github.com/fsouza/go-dockerclient" @@ -35,9 +34,7 @@ func TestCreate(t *testing.T) { gomock.InOrder( mockOS.EXPECT().MkdirAll(gomock.Any(), gomock.Any()).Return(nil), - mockOS.EXPECT().OpenFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, errors.New("err")), - mockOS.EXPECT().IsNotExist(gomock.Any()).Return(true), - mockOS.EXPECT().Create(gomock.Any()).Return(mockFile, nil), + mockOS.EXPECT().OpenFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockFile, nil), mockFile.EXPECT().Write(gomock.Any()).Return(0, nil), mockFile.EXPECT().Sync().Return(nil), mockFile.EXPECT().Close().Return(nil), diff --git a/agent/containermetadata/write_metadata_windows.go b/agent/containermetadata/write_metadata_windows.go index 2201ac3324a..f4b051fd905 100644 --- a/agent/containermetadata/write_metadata_windows.go +++ b/agent/containermetadata/write_metadata_windows.go @@ -51,17 +51,12 @@ func writeToMetadataFile(osWrap oswrapper.OS, ioutilWrap ioutilwrapper.IOUtil, d } metadataFileName := filepath.Join(metadataFileDir, metadataFile) - file, err := osWrap.OpenFile(metadataFileName, os.O_WRONLY, metadataPerm) + file, err := osWrap.OpenFile(metadataFileName, os.O_WRONLY|os.O_CREATE, metadataPerm) if err != nil { - // Retry if file does not exist - if osWrap.IsNotExist(err) { - file, err = osWrap.Create(metadataFileName) - } - if err != nil { - return err - } + return err } defer file.Close() + _, err = file.Write(data) if err != nil { return err diff --git a/agent/containermetadata/write_metadata_windows_test.go b/agent/containermetadata/write_metadata_windows_test.go index 1247449b3d6..084bf678fe5 100644 --- a/agent/containermetadata/write_metadata_windows_test.go +++ b/agent/containermetadata/write_metadata_windows_test.go @@ -35,7 +35,6 @@ func TestWriteOpenFileFail(t *testing.T) { gomock.InOrder( mockOS.EXPECT().OpenFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, mockOpenErr), - mockOS.EXPECT().IsNotExist(mockOpenErr).Return(false), ) err := writeToMetadataFile(mockOS, nil, mockData, mockTaskARN, mockContainerName, mockDataDir) diff --git a/agent/engine/docker_task_engine.go b/agent/engine/docker_task_engine.go index 55ad70b0518..774d054b209 100644 --- a/agent/engine/docker_task_engine.go +++ b/agent/engine/docker_task_engine.go @@ -342,7 +342,7 @@ func (engine *DockerTaskEngine) sweepTask(task *api.Task) { if engine.cfg.ContainerMetadataEnabled { err := engine.metadataManager.Clean(task.Arn) if err != nil { - seelog.Errorf("Clean task metadata failed for task %s: %v", task, err) + seelog.Warnf("Clean task metadata failed for task %s: %v", task, err) } } engine.saver.Save() @@ -660,7 +660,7 @@ func (engine *DockerTaskEngine) createContainer(task *api.Task, container *api.C if engine.cfg.ContainerMetadataEnabled && !container.IsInternal() { mderr := engine.metadataManager.Create(config, hostConfig, task.Arn, container.Name) if mderr != nil { - seelog.Errorf("Create metadata failed for container %s of task %s: %v", container, task, mderr) + seelog.Warnf("Create metadata failed for container %s of task %s: %v", container, task, mderr) } } @@ -702,7 +702,7 @@ func (engine *DockerTaskEngine) startContainer(task *api.Task, container *api.Co go func() { err := engine.metadataManager.Update(dockerContainer.DockerID, task.Arn, container.Name) if err != nil { - seelog.Errorf("Update metadata file failed for container %s of task %s: %v", container, task, err) + seelog.Warnf("Update metadata file failed for container %s of task %s: %v", container, task, err) } else { container.SetMetadataFileUpdated() seelog.Debugf("Updated metadata file for container %s of task %s", container, task) diff --git a/agent/functional_tests/testdata/taskdefinitions/mdservice-validator-windows/task-definition.json b/agent/functional_tests/testdata/taskdefinitions/mdservice-validator-windows/task-definition.json new file mode 100644 index 00000000000..6d81554166c --- /dev/null +++ b/agent/functional_tests/testdata/taskdefinitions/mdservice-validator-windows/task-definition.json @@ -0,0 +1,12 @@ +{ + "family": "ecsftest-mdservice-validator", + "networkMode": "none", + "containerDefinitions": [{ + "image": "microsoft/windowsservercore:latest", + "name": "mdservice-validator-windows", + "cpu": 100, + "memory": 100, + "entryPoint": ["powershell"], + "command": ["-c", "sleep 10; if($?){if(cat $env:ECS_CONTAINER_METADATA_FILE | Select-String -pattern READY){exit 42}else {exit 1}};"] + }] +} diff --git a/agent/functional_tests/tests/functionaltests_windows_test.go b/agent/functional_tests/tests/functionaltests_windows_test.go index 552bcada8d9..29401eedcb7 100644 --- a/agent/functional_tests/tests/functionaltests_windows_test.go +++ b/agent/functional_tests/tests/functionaltests_windows_test.go @@ -186,3 +186,40 @@ func taskIamRolesTest(networkMode string, agent *TestAgent, t *testing.T) { } } + +// TestMetadataServiceValidator Tests that the metadata file can be accessed from the +// container using the ECS_CONTAINER_METADATA_FILE environment variables +func TestMetadataServiceValidator(t *testing.T) { + agentOptions := &AgentOptions{ + ExtraEnvironment: map[string]string{ + "ECS_ENABLE_CONTAINER_METADATA": "true", + }, + } + + agent := RunAgent(t, agentOptions) + defer agent.Cleanup() + + tdOverride := make(map[string]string) + tdOverride["$$$TEST_REGION$$$"] = *ECS.Config.Region + tdOverride["$$$NETWORK_MODE$$$"] = "" + + task, err := agent.StartTaskWithTaskDefinitionOverrides(t, "mdservice-validator-windows", tdOverride) + if err != nil { + t.Fatalf("Error starting mdservice-validator-windows: %v", err) + } + + // clean up + err = task.WaitStopped(2 * time.Minute) + require.NoError(t, err, "Error waiting for task to transition to STOPPED") + + containerID, err := agent.ResolveTaskDockerID(task, "mdservice-validator-windows") + if err != nil { + t.Fatalf("Error resolving docker id for container in task: %v", err) + } + + containerMetaData, err := agent.DockerClient.InspectContainer(containerID) + require.NoError(t, err, "Could not inspect container for task") + + exitCode := containerMetaData.State.ExitCode + assert.Equal(t, 42, exitCode, fmt.Sprintf("Expected exit code of 42; got %d", exitCode)) +} diff --git a/agent/functional_tests/util/utils_windows.go b/agent/functional_tests/util/utils_windows.go index 8748ae6f7a4..0bffb7ca99e 100644 --- a/agent/functional_tests/util/utils_windows.go +++ b/agent/functional_tests/util/utils_windows.go @@ -115,6 +115,11 @@ func (agent *TestAgent) StopAgent() error { } func (agent *TestAgent) StartAgent() error { + if agent.Options != nil { + for k, v := range agent.Options.ExtraEnvironment { + os.Setenv(k, v) + } + } agentInvoke := exec.Command(".\\agent.exe") if TestDirectory := os.Getenv("ECS_WINDOWS_TEST_DIR"); TestDirectory != "" { agentInvoke.Dir = TestDirectory @@ -130,6 +135,11 @@ func (agent *TestAgent) StartAgent() error { } func (agent *TestAgent) Cleanup() { + if agent.Options != nil { + for k, _ := range agent.Options.ExtraEnvironment { + os.Unsetenv(k) + } + } key, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Amazon\ECS Agent\State File`, registry.ALL_ACCESS) if err != nil { return diff --git a/agent/statemanager/state_manager.go b/agent/statemanager/state_manager.go index f6a79eb4b84..fa558de0f04 100644 --- a/agent/statemanager/state_manager.go +++ b/agent/statemanager/state_manager.go @@ -48,7 +48,7 @@ const ( // b) Add 'ContainerResourcesProvisioned' as a new 'ContainerStatus' enum // c) Add 'SteadyStateStatus' field to 'Container' struct // d) Add 'ENIAttachments' struct - // e) Add 'MetadataUpdated' field to 'api.Container' + // e) Add 'MetadataUpdated' field to 'api.Container' ECSDataVersion = 6 // ecsDataFile specifies the filename in the ECS_DATADIR