Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Windows service #1070

Merged
merged 11 commits into from
Nov 15, 2017
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ out/
coverage/
_bin/
*.exe
*.exe~
*.zip
*.swp
*.orig
Expand All @@ -15,3 +16,4 @@ _bin/
/misc/windows-iam/devcon*
/misc/pause-container/pause
*-stamp
/.idea/
62 changes: 52 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,26 +48,68 @@ See also the Advanced Usage section below.

### On Windows Server 2016

On Windows Server 2016, the Amazon ECS Container Agent runs as a process on the
host. Unlike Linux, the agent may not run inside a container as it uses the
host's registry and the named pipe at `\\.\pipe\docker_engine` to communicate
with the Docker daemon.
On Windows Server 2016, the Amazon ECS Container Agent runs as a process or
service on the host. Unlike Linux, the agent may not run inside a container as
it uses the host's registry and the named pipe at `\\.\pipe\docker_engine` to
communicate with the Docker daemon.

#### As a Service
To install the service, you can do the following:

```powershell
PS C:\> # Set up directories the agent uses
PS C:\> New-Item -Type directory -Path ${env:ProgramFiles}\Amazon\ECS -Force
PS C:\> New-Item -Type directory -Path ${env:ProgramData}\Amazon\ECS -Force
PS C:\> # Set up configuration
PS C:\> $ecsExeDir = "${env:ProgramFiles}\Amazon\ECS"
PS C:\> [Environment]::SetEnvironmentVariable("ECS_CLUSTER", "my-windows-cluster", "Machine")
PS C:\> [Environment]::SetEnvironmentVariable("ECS_LOGFILE", "${env:ProgramData}\Amazon\ECS\log\ecs-agent.log", "Machine")
PS C:\> [Environment]::SetEnvironmentVariable("ECS_DATADIR", "${env:ProgramData}\Amazon\ECS\data", "Machine")
PS C:\> # Download the agent
PS C:\> $agentVersion = "latest"
PS C:\> $agentZipUri = "https://s3.amazonaws.com/amazon-ecs-agent/ecs-agent-windows-$agentVersion.zip"
PS C:\> $zipFile = "${env:TEMP}\ecs-agent.zip"
PS C:\> Invoke-RestMethod -OutFile $zipFile -Uri $agentZipUri
PS C:\> # Put the executables in the executable directory.
PS C:\> Expand-Archive -Path $zipFile -DestinationPath $ecsExeDir -Force
PS C:\> Set-Location ${ecsExeDir}
PS C:\> # Set $EnableTaskIAMRoles to $true to enable task IAM roles
PS C:\> # Note that enabling IAM roles will make port 80 unavailable for tasks.
PS C:\> [bool]$EnableTaskIAMRoles = $false
PS C:\> if (${EnableTaskIAMRoles} {
>> .\hostsetup.ps1
>> }
PS C:\> # Install the agent service
PS C:\> New-Service -Name "AmazonECS" `
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't it confusing the call the ECS Agent service "AmazonECS"? This is the agent. Would it be better to call it "AmazonECSAgent"?

Sorry if this topic came up before, feel free to ignore & point me to any existing discussion.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I named it "AmazonECS" so that we'd have the flexibility to swap out a bit in terms of implementation without requiring customers to change the name that they're using or making the name confusing. Specifically, this enables us to potentially transition to running the agent inside a container and using a service on the host to manage the container (similar to what we do with ecs-init on Linux). If we called it "AmazonECSAgent", we could still do that but it could create confusion because the service is separate from the agent.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AmazonECSService?

-BinaryPathName "$ecsExeDir\amazon-ecs-agent.exe -windows-service" `
-DisplayName "Amazon ECS" `
-Description "Amazon ECS service runs the Amazon ECS agent" `
-DependsOn Docker `
-StartupType Manual
```

To run the service, you can do the following:
```powershell
Start-Service AmazonECS
```

#### As a Process
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to continue to support this? Whats the thought here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I'd like to continue documenting this at least until we vend an easier way to install the agent.


```powershell
PS C:\> # Set up directories the agent uses
PS C:\> New-Item -Type directory -Path $ProgramFiles\Amazon\ECS
PS C:\> New-Item -Type directory -Path $ProgramData\Amazon\ECS
PS C:\> New-Item -Type directory -Path ${env:ProgramFiles}\Amazon\ECS -Force
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm going to leave this as-is for now; we can come back and fix it later.

PS C:\> New-Item -Type directory -Path ${env:ProgramData}\Amazon\ECS -Force
PS C:\> # Set up configuration
PS C:\> $ecsExeDir = "$env:ProgramFiles\Amazon\ECS"
PS C:\> $ecsExeDir = "${env:ProgramFiles}\Amazon\ECS"
PS C:\> [Environment]::SetEnvironmentVariable("ECS_CLUSTER", "my-windows-cluster", "Machine")
PS C:\> [Environment]::SetEnvironmentVariable("ECS_LOGFILE", "$ProgramData\Amazon\ECS\log\ecs-agent.log", "Machine")
PS C:\> [Environment]::SetEnvironmentVariable("ECS_DATADIR", "$ProgramData\Amazon\ECS\data", "Machine")
PS C:\> [Environment]::SetEnvironmentVariable("ECS_LOGFILE", "${env:ProgramData}\Amazon\ECS\log\ecs-agent.log", "Machine")
PS C:\> [Environment]::SetEnvironmentVariable("ECS_DATADIR", "${env:ProgramData}\Amazon\ECS\data", "Machine")
PS C:\> # Set this environment variable to "true" to enable IAM roles. Note that enabling IAM roles will make port 80 unavailable for tasks.
PS C:\> [Environment]::SetEnvironmentVariable("ECS_ENABLE_TASK_IAM_ROLE", "false", "Machine")
PS C:\> # Download the agent
PS C:\> $agentVersion = "latest"
PS C:\> $agentZipUri = "https://s3.amazonaws.com/amazon-ecs-agent/ecs-agent-windows-$agentVersion.zip"
PS C:\> $zipFile = "$env:TEMP\ecs-agent.zip"
PS C:\> $zipFile = "${env:TEMP}\ecs-agent.zip"
PS C:\> Invoke-RestMethod -OutFile $zipFile -Uri $agentZipUri
PS C:\> # Put the executables in the executable directory.
PS C:\> Expand-Archive -Path $zipFile -DestinationPath $ecsExeDir -Force
Expand Down
4 changes: 2 additions & 2 deletions agent/Gopkg.lock

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

22 changes: 16 additions & 6 deletions agent/app/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@ import (
"github.com/aws/amazon-ecs-agent/agent/utils"
"github.com/aws/amazon-ecs-agent/agent/version"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
aws_credentials "github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/defaults"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/cihub/seelog"
)

Expand Down Expand Up @@ -76,8 +76,12 @@ type agent interface {
// printECSAttributes prints the Agent's capabilities based on
// its environment
printECSAttributes() int
// startWindowsService starts the agent as a Windows Service
startWindowsService() int
// start starts the Agent execution
start() int
// setTerminationHandler sets the termination handler
setTerminationHandler(sighandlers.TerminationHandler)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

neat!

}

// ecsAgent wraps all the entities needed to start the ECS Agent execution.
Expand All @@ -100,9 +104,10 @@ type ecsAgent struct {
mac string
metadataManager containermetadata.Manager
resource resources.Resource
terminationHandler sighandlers.TerminationHandler
}

// newAgent returns a new ecsAgent object
// newAgent returns a new ecsAgent object, but does not start anything
func newAgent(
ctx context.Context,
blackholeEC2Metadata bool,
Expand Down Expand Up @@ -158,9 +163,10 @@ func newAgent(
PluginsPath: cfg.CNIPluginsPath,
MinSupportedCNIVersion: config.DefaultMinSupportedCNIVersion,
}),
os: oswrapper.New(),
metadataManager: metadataManager,
resource: resources.New(),
os: oswrapper.New(),
metadataManager: metadataManager,
resource: resources.New(),
terminationHandler: sighandlers.StartDefaultTerminationHandler,
}, nil
}

Expand All @@ -184,6 +190,10 @@ func (agent *ecsAgent) printECSAttributes() int {
return exitcodes.ExitSuccess
}

func (agent *ecsAgent) setTerminationHandler(handler sighandlers.TerminationHandler) {
agent.terminationHandler = handler
}

// start starts the ECS Agent
func (agent *ecsAgent) start() int {
sighandlers.StartDebugHandler()
Expand Down Expand Up @@ -514,7 +524,7 @@ func (agent *ecsAgent) startAsyncRoutines(
go imageManager.StartImageCleanupProcess(agent.ctx)
}

go sighandlers.StartTerminationHandler(stateManager, taskEngine)
go agent.terminationHandler(stateManager, taskEngine)

// Agent introspection api
go handlers.ServeHttp(&agent.containerInstanceARN, taskEngine, agent.cfg)
Expand Down
6 changes: 6 additions & 0 deletions agent/app/agent_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ var awsVPCCNIPlugins = []string{ecscni.ECSENIPluginName,
ecscni.ECSIPAMPluginName,
}

// startWindowsService is not supported on Linux
func (agent *ecsAgent) startWindowsService() int {
seelog.Error("Windows Services are not supported on Linux")
return 1
}

// initializeTaskENIDependencies initializes all of the dependencies required by
// the Agent to support the 'awsvpc' networking mode. A non nil error is returned
// if an error is encountered during this process. An additional boolean flag to
Expand Down
5 changes: 5 additions & 0 deletions agent/app/agent_unix_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import (
"github.com/aws/amazon-ecs-agent/agent/eventstream"
"github.com/aws/amazon-ecs-agent/agent/resources/mock_resources"
"github.com/aws/amazon-ecs-agent/agent/sighandlers/exitcodes"
"github.com/aws/amazon-ecs-agent/agent/statemanager"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/golang/mock/gomock"
Expand Down Expand Up @@ -100,6 +101,7 @@ func TestDoStartHappyPath(t *testing.T) {
cfg: &cfg,
credentialProvider: credentials.NewCredentials(mockCredentialsProvider),
dockerClient: dockerClient,
terminationHandler: func(saver statemanager.Saver, taskEngine engine.TaskEngine) {},
}

go agent.doStart(eventstream.NewEventStream("events", ctx),
Expand Down Expand Up @@ -198,6 +200,7 @@ func TestDoStartTaskENIHappyPath(t *testing.T) {
cniClient: cniClient,
os: mockOS,
ec2MetadataClient: mockMetadata,
terminationHandler: func(saver statemanager.Saver, taskEngine engine.TaskEngine) {},
}

go agent.doStart(eventstream.NewEventStream("events", ctx),
Expand Down Expand Up @@ -497,6 +500,7 @@ func TestDoStartCgroupInitHappyPath(t *testing.T) {
credentialProvider: credentials.NewCredentials(mockCredentialsProvider),
dockerClient: dockerClient,
resource: mockResource,
terminationHandler: func(saver statemanager.Saver, taskEngine engine.TaskEngine) {},
}

go agent.doStart(eventstream.NewEventStream("events", ctx),
Expand Down Expand Up @@ -537,6 +541,7 @@ func TestDoStartCgroupInitErrorPath(t *testing.T) {
credentialProvider: credentials.NewCredentials(mockCredentialsProvider),
dockerClient: dockerClient,
resource: mockResource,
terminationHandler: func(saver statemanager.Saver, taskEngine engine.TaskEngine) {},
}

status := agent.doStart(eventstream.NewEventStream("events", ctx),
Expand Down
Loading