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

Support Docker API v1.24 on windows #783

Merged
merged 2 commits into from
Jun 15, 2017

Conversation

petderek
Copy link
Contributor

Summary

Enables docker API v1.24 for windows, and sets it as the default version.

Implementation details

Refactored some of the versioning and docker client code.

Testing

  • Builds on Linux (make release)
  • Builds on Windows (go build -out amazon-ecs-agent.exe ./agent)
  • Unit tests on Linux (make test) pass
  • Unit tests on Windows (go test -timeout=25s ./agent/...) pass
  • Integration tests on Linux (make run-integ-tests) pass
  • Integration tests on Windows (.\scripts\run-integ-tests.ps1) pass
  • Functional tests on Linux (make run-functional-tests) pass
  • Functional tests on Windows (.\scripts\run-functional-tests.ps1) pass

New tests cover the changes: no

Description for the changelog

Moving docker API to 1.24 for windows

Licensing

This contribution is under the terms of the Apache 2.0 License: yes

Copy link
Contributor

@cabbruzzese cabbruzzese left a comment

Choose a reason for hiding this comment

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

lgtm

docker "github.com/fsouza/go-dockerclient"
)

type Factory interface {
Copy link
Contributor

Choose a reason for hiding this comment

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

lint: // Factory ...

GetClient(version DockerVersion) (dockeriface.Client, error)

// FindSupportedAPIVersions tests each agent supported API version
// against the docker daemon and returns a slice of supported
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: Docker

return docker.NewVersionedClient(endpoint, version)
}

func NewFactory(endpoint string) Factory {
Copy link
Contributor

Choose a reason for hiding this comment

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

lint: //NewFactory ...

default:
t.Fatal("Unrecognized version")
client, ok := mockClients[version]
if !ok {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you use assert.True(t, ok) instead?

if len(versions) != len(expectedVersions) {
t.Errorf("Expected %d versions but got %d", len(expectedVersions), len(versions))
actualVersions := factory.FindSupportedAPIVersions()
if len(expectedVersions) != len(actualVersions) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you use assert.Equal(t, exptectedVersions, actualVersions) or a variation of it?

FluentdDriver LoggingDriver = "fluentd"
AwslogsDriver LoggingDriver = "awslogs"
SplunklogsDriver LoggingDriver = "splunk"
JsonFileDriver LoggingDriver = "json-file"
Copy link
Contributor

Choose a reason for hiding this comment

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

While you're here, %s/JsonFileDriver/JSONFileDriver

JournaldDriver LoggingDriver = "journald"
GelfDriver LoggingDriver = "gelf"
FluentdDriver LoggingDriver = "fluentd"
AwslogsDriver LoggingDriver = "awslogs"
Copy link
Contributor

Choose a reason for hiding this comment

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

lint for this as well AWSLogsDriver

@@ -19,7 +19,7 @@ Invoke-Expression "${PSScriptRoot}\..\misc\image-cleanup-test-images\build.ps1"
$cwd = (pwd).Path
try {
cd "${PSScriptRoot}"
go test -tags integration -timeout=10m -v ../agent/engine ../agent/stats
go test -tags integration -timeout=15m -v ../agent/engine ../agent/stats
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this resolve #759 as well?

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 had a chat with @adnxn yesterday. The answer is maybe. I want to see if there is a way to run this test more deterministically.

@aaithal aaithal mentioned this pull request Apr 28, 2017
return client, nil
}

func (f *factory) FindSupportedAPIVersions() []DockerVersion {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you add a comment for this?

// FindSupportedAPIVersions returns the API versions which are both known to exist for the Docker daemon to which the agent is connected and are supported by the agent.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Does it need to be on both the interface and the function?

newVersionedClient = func(endpoint, version string) (dockeriface.Client, error) {
if endpoint != expectedEndpoint {
t.Errorf("Expected endpoint %s but was %s", expectedEndpoint, endpoint)
}
if version != string(defaultVersion) {
t.Errorf("Expected version %s but was %s", defaultVersion, version)
if version != string(getDefaultVersion()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Since you're in this file...want to change it to use github.com/stretchr/testify/assert?

assert.Equal(t, string(getDefaultVersion()), version)

func init() {
supportedVersions = []DockerVersion{
// getKnownAPIVersions returns all of the API versions that we know about.
// It doesn't care if the version is supported by docker or ECS agent
Copy link
Contributor

Choose a reason for hiding this comment

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

s/docker/Docker/

Version_1_21,
Version_1_22,
Version_1_23,
Version_1_24,
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is 1.24 included on Windows but not Linux? Should this be 1.23 on both instead?

// running with a build tag
func TestTest(t *testing.T) {}
func getDefaultVersion() DockerVersion {
return Version_1_24
Copy link
Contributor

Choose a reason for hiding this comment

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

I think the minimum on Windows is 1.23.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're right. I didn't do the other things mentioned on #687, I can address that separately

@@ -1,3 +1,4 @@
// +build windows
Copy link
Contributor

Choose a reason for hiding this comment

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

I have no idea why git thinks you moved a file here...did you intentionally get rid of dummy_test.go?

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! Dummy Tests don't appear to be needed anymore.

@@ -1,22 +0,0 @@
// Copyright 2014-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
Copy link
Contributor

Choose a reason for hiding this comment

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

How come this file is removed?

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 didn't have trouble running the functional tests when this file was excluded.

@@ -0,0 +1,116 @@
// Copyright 2014-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.

Choose a reason for hiding this comment

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

Header: 2017 Amaazon.com

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Doh! I'll fix it.

Version_1_25,
Version_1_26,
Version_1_27,
Version_1_28,

Choose a reason for hiding this comment

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

Is there any reason put the 25-28 here, while agent doesn't support?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah! Its for future use cases. In the interface for the factory, we only currently expose what is supported by both agent and docker.

@samuelkarp had mentioned to me that it would be useful to understand all of the API versions that the host's docker daemon supports even if we don't leverage it to the main engine code.

@@ -207,7 +207,7 @@ func (agent *TestAgent) StartAgent() error {
return errors.New("Could not inspect agent container: " + err.Error())
}
agent.IntrospectionURL = "http://localhost:" + containerMetadata.NetworkSettings.Ports["51678/tcp"][0].HostPort
err = agent.platformIndependentStartAgent()
err = agent.verifyIntrospectionAPI()
Copy link
Contributor

Choose a reason for hiding this comment

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

minor: you can just do return agent.verifyIntrospectionAPI() here.

Copy link
Contributor

@aaithal aaithal left a comment

Choose a reason for hiding this comment

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

Neat stuff! Some of the comments are me linting stuff. If we could do the getClient refactor, this would be good to go.

)

// We replace docker APIs 1.17 - 12.23 on the windows agent
const NUMBER_OF_REPLACED_VERSIONS = 7
Copy link
Contributor

Choose a reason for hiding this comment

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

GoStyle: NumberOfReplacedVersions. Also, this doesn't need to be global scoped.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Re-factoring allowed me to test this differently: I removed this line

"github.com/aws/amazon-ecs-agent/agent/engine/dockeriface"
)

const MIN_DOCKER_API_WINDOWS = Version_1_24
Copy link
Contributor

Choose a reason for hiding this comment

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

GoStyle: MinDockerAPIWindows

"github.com/aws/amazon-ecs-agent/agent/engine/dockeriface"
)

func (f *factory) GetClient(version DockerVersion) (dockeriface.Client, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

// GetClient gets ...


const MIN_DOCKER_API_WINDOWS = Version_1_24

// Override newVersionedClient. We need this because agent makes assumptions
Copy link
Contributor

Choose a reason for hiding this comment

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

// GetClient overridses ..

return client, nil
}

f.lock.Lock()
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should refactor this method to not hold the lock while we do expensive operations like client.Ping(). Can you instead add get and set methods for the map and acquire the lock only when accessing the lock?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sounds good. I changed this so that agent detect all API versions on init. This lets me get rid of the lock entirely.

@@ -115,7 +115,7 @@ type AgentOptions struct {
}

// PLatform Independent piece of Agent Startup. Gets executed on both linux and Windows.
Copy link
Contributor

Choose a reason for hiding this comment

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

While you're here, can you also fix the comment? // verifyIntrospectionAPI verifies ...

@@ -127,7 +127,7 @@ func (agent *TestAgent) StartAgent() error {
}
agent.Process = agentInvoke.Process
agent.IntrospectionURL = "http://localhost:51678"
err = agent.platformIndependentStartAgent()
err = agent.verifyIntrospectionAPI()
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: can just be return agent.verifyIntrospectionAPI()

route -p delete 169.254.170.2/32
# This address will only exist on systems that have already set up the routes
$ip = (Get-NetIPAddress -InterfaceIndex $ifIndex -IPAddress 169.254.170.2 -PrefixLength 32)
if(!($ip)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

As a windows noob, I'd really appreciate it if you could add a comment for each line here.

Add-VMNetworkAdapter -VMNetworkAdapterName "APIPA" -SwitchName nat -ManagementOS
$ifIndex = (Get-NetAdapter -Name "*APIPA*" | Sort-Object | Select ifIndex).ifindex
New-NetIPAddress -InterfaceIndex $ifIndex -IPAddress 169.254.170.2 -PrefixLength 32
New-NetRoute -DestinationPrefix 172.20.128.0/20 -ifIndex $ifindex
Copy link
Contributor

@khannag khannag Jun 2, 2017

Choose a reason for hiding this comment

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

I do not think we should hard code the 172.20.128.0/20 here. The value corresponds to the Subnet CIDR under the IPAM section for the default nat network. Essentially, obtained from the output of docker network inspect nat (default network). I am thinking we should discover the value rather than hardcode it here.

# Enable NAT for container-to-host communication
Add-VMNetworkAdapter -VMNetworkAdapterName "APIPA" -SwitchName nat -ManagementOS
$ifIndex = (Get-NetAdapter -Name "*APIPA*" | Sort-Object | Select ifIndex).ifindex
New-NetIPAddress -InterfaceIndex $ifIndex -IPAddress 169.254.170.2 -PrefixLength 32
Copy link
Contributor

@khannag khannag Jun 2, 2017

Choose a reason for hiding this comment

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

The only thing which may require a change around reboots is the piece which involves ifIndex - For instance, what if someone adds a second ENI prior to Windows rebooting and running this startup script. The interface index could change in that situation. I dont know though as to how common this possibility or similar possibilities like this are.

So, to handle reboot case: You can change the script to check if a route of the below from currently exists - using Get-NetRoute -DestinationPrefix 172.20.128.0/20. If it exists, you could delete it first using Remove-NetRoute and then add it using the right interface index which will be discovered as done in line 18 above.

@@ -78,18 +78,7 @@ try {

if([System.Environment]::GetEnvironmentVariable("ECS_ENABLE_TASK_IAM_ROLE", "Machine") -eq "true") {
LogMsg "IAM roles environment variable is set."

.\loopback.ps1
.\hostsetup.ps1
Copy link
Contributor

@khannag khannag Jun 2, 2017

Choose a reason for hiding this comment

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

Does the IAM role test failing issue you mentioned to me with docker 17.03.1-ee-3, get resolved now ?

Copy link
Contributor

@khannag khannag left a comment

Choose a reason for hiding this comment

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

My comments on the proposed changes below,

@@ -0,0 +1,96 @@
// +build !integration
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this !integration just so that the tests don't run multiple times?


newVersionedClient = func(endpoint, version string) (dockeriface.Client, error) {
mockClient := mock_dockeriface.NewMockClient(ctrl)
if version == string(MinDockerAPIWindows) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Rather than doing this where you assert.Equal(t, expectedClient, actualClient), why not assert.Equal(t, MinDockerAPIWindows, version)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This function is called for every client at agent start-up -- assertion that way won't work anymore.

func (f *factory) GetClient(version DockerVersion) (dockeriface.Client, error) {
for _, v := range getWindowsReplaceableVersions() {
if v == version {
version = MinDockerAPIWindows
Copy link
Contributor

Choose a reason for hiding this comment

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

break?

Copy link
Contributor

@khannag khannag left a comment

Choose a reason for hiding this comment

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

LGTM.

@samuelkarp
Copy link
Contributor

Can you rebase and update go-dockerclient to include fsouza/go-dockerclient#654?

@petderek
Copy link
Contributor Author

@samuelkarp 👍 I'm working on that now

@petderek petderek force-pushed the windows-api-versioning branch from 09be263 to 266983f Compare June 15, 2017 18:45
Copy link
Contributor

@aaithal aaithal left a comment

Choose a reason for hiding this comment

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

Looks good overall. I have a few minor comments.

if ok {
return client, nil
} else {
return nil, errors.New("Client not found for docker version: " + string(version))
Copy link
Contributor

Choose a reason for hiding this comment

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

Error messages should be begin with lowercase. It would also be great to include some context here: "docker client factory: client not found..."

// Validate that agentVersions is a subset of allVersions
for _, agentVersion := range getAgentVersions() {
if !isKnown(agentVersion) {
t.Errorf("Version not found in known Docker Versions: %s", agentVersion)
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you use assert.True here instead?

}

factory := NewFactory(expectedEndpoint)
actualClient, _ := factory.GetClient(Version_1_19)
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you also do assert.NoError(t, err) by catching the error when it's returned?

Copy link
Contributor

@samuelkarp samuelkarp left a comment

Choose a reason for hiding this comment

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

Looks good after you address @aaithal's feedback.

Copy link
Contributor

@aaithal aaithal left a comment

Choose a reason for hiding this comment

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

:shipit:

petderek added 2 commits June 15, 2017 14:20
* Separates docker API version support for windows
* Sets docker API version default to 1.23 for windows
@petderek petderek force-pushed the windows-api-versioning branch from 36c4da9 to 1cb0001 Compare June 15, 2017 21:23
@petderek petderek merged commit 1cb0001 into aws:dev Jun 15, 2017
@samuelkarp samuelkarp added this to the 1.14.4 milestone Jul 3, 2017
@petderek petderek deleted the windows-api-versioning branch August 15, 2017 19:15
@petderek petderek restored the windows-api-versioning branch August 15, 2017 19:15
@petderek petderek deleted the windows-api-versioning branch October 10, 2017 21:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants