Skip to content

Commit b026db7

Browse files
committed
dockerclient: Agent supported Docker versions from client min,API Versions
dockerclient: Finding agent supported Docker versions by getting the default client version and if the min API version is present, add all the clients between min API version and API version supported, else follow current logic utils: reuse the semantic version comparator to compare Docker API versions with only major and minor versions too
1 parent 1045da5 commit b026db7

6 files changed

+119
-3
lines changed

agent/engine/dockerclient/dockerclientfactory.go

+60
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,19 @@ import (
1717
"errors"
1818

1919
"github.com/aws/amazon-ecs-agent/agent/engine/dockeriface"
20+
"github.com/aws/amazon-ecs-agent/agent/utils"
2021
log "github.com/cihub/seelog"
2122
docker "github.com/fsouza/go-dockerclient"
2223
)
2324

25+
const (
26+
// minDockerAPILinux is the min Docker API version supported by agent
27+
minDockerAPILinux = Version_1_17
28+
// minAPIVersionKey is the docker.Env key for min API version
29+
minAPIVersionKey = "MinAPIVersion"
30+
// apiVersionKey is the docker.Env key for API version
31+
apiVersionKey = "ApiVersion"
32+
)
2433
// Factory provides a collection of docker remote clients that include a
2534
// recommended client version as well as a set of alternative supported
2635
// docker clients.
@@ -110,6 +119,11 @@ func (f *factory) getClient(version DockerVersion) (dockeriface.Client, error) {
110119
// findDockerVersions loops over all known API versions and finds which ones
111120
// are supported by the docker daemon on the host
112121
func findDockerVersions(endpoint string) map[DockerVersion]dockeriface.Client {
122+
// if the client version returns a min version and api version, then use it to return
123+
// Docker versions
124+
if clients, ok := findDockerVersionsfromMinMaxVersions(endpoint); ok {
125+
return clients
126+
}
113127
clients := make(map[DockerVersion]dockeriface.Client)
114128
for _, version := range getKnownAPIVersions() {
115129
client, err := newVersionedClient(endpoint, string(version))
@@ -121,8 +135,54 @@ func findDockerVersions(endpoint string) map[DockerVersion]dockeriface.Client {
121135
err = client.Ping()
122136
if err != nil {
123137
log.Infof("Failed to ping with Docker version %s: %v", version, err)
138+
continue
124139
}
125140
clients[version] = client
126141
}
127142
return clients
128143
}
144+
145+
func findDockerVersionsfromMinMaxVersions(endpoint string) (map[DockerVersion]dockeriface.Client, bool) {
146+
clients := make(map[DockerVersion]dockeriface.Client)
147+
// get a Docker client with the default supported version
148+
client, err := newVersionedClient(endpoint, string(minDockerAPILinux))
149+
if err != nil {
150+
log.Infof("Error while creating client: %v", err)
151+
return nil, false
152+
}
153+
154+
clientVersion, err := client.Version()
155+
if err != nil {
156+
log.Infof("Error while getting client version: %v", err)
157+
return nil, false
158+
}
159+
160+
// check if the docker.Env obj has MinAPIVersion key
161+
if clientVersion.Exists(minAPIVersionKey) {
162+
minAPIVersion := clientVersion.Get(minAPIVersionKey)
163+
apiVersion := clientVersion.Get(apiVersionKey)
164+
for _, version := range getKnownAPIVersions() {
165+
lessThanMinCheck := "<"+minAPIVersion
166+
moreThanMaxCheck := ">"+apiVersion
167+
minVersionCheck, minErr := utils.Version(version).Matches(lessThanMinCheck)
168+
maxVersionCheck, maxErr := utils.Version(version).Matches(moreThanMaxCheck)
169+
if minErr != nil || maxErr != nil {
170+
log.Infof("Error while comparing Docker API versions: %v, %v", minErr, maxErr)
171+
continue
172+
}
173+
// do not add the version when it is less than min api version or greater
174+
// than api version
175+
if minVersionCheck || maxVersionCheck {
176+
continue
177+
}
178+
client, err := newVersionedClient(endpoint, string(version))
179+
if err != nil {
180+
log.Infof("Error while creating client: %v", err)
181+
continue
182+
}
183+
clients[version] = client
184+
}
185+
return clients, true
186+
}
187+
return nil, false
188+
}

agent/engine/dockerclient/dockerclientfactory_test.go

+36-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"github.com/aws/amazon-ecs-agent/agent/engine/dockeriface/mocks"
2222
"github.com/golang/mock/gomock"
2323
"github.com/stretchr/testify/assert"
24+
docker "github.com/fsouza/go-dockerclient"
2425
)
2526

2627
const expectedEndpoint = "expectedEndpoint"
@@ -35,7 +36,8 @@ func TestGetDefaultClientSuccess(t *testing.T) {
3536
if version == string(getDefaultVersion()) {
3637
mockClient = expectedClient
3738
}
38-
mockClient.EXPECT().Ping()
39+
mockClient.EXPECT().Version().Return(&docker.Env{}, nil).AnyTimes()
40+
mockClient.EXPECT().Ping().AnyTimes()
3941

4042
return mockClient, nil
4143
}
@@ -59,6 +61,7 @@ func TestFindSupportedAPIVersions(t *testing.T) {
5961
// Ensure that agent pings all known versions of Docker API
6062
for i := 0; i < len(allVersions); i++ {
6163
mockClients[string(allVersions[i])] = mock_dockeriface.NewMockClient(ctrl)
64+
mockClients[string(allVersions[i])].EXPECT().Version().Return(&docker.Env{}, nil).AnyTimes()
6265
mockClients[string(allVersions[i])].EXPECT().Ping()
6366
}
6467

@@ -92,3 +95,35 @@ func TestVerifyAgentVersions(t *testing.T) {
9295
assert.True(t, isKnown(agentVersion))
9396
}
9497
}
98+
99+
func TestFindSupportedAPIVersionsFromMinAPIVersions(t *testing.T) {
100+
ctrl := gomock.NewController(t)
101+
defer ctrl.Finish()
102+
103+
agentVersions := getAgentVersions()
104+
allVersions := getKnownAPIVersions()
105+
106+
// Set up the mocks and expectations
107+
mockClients := make(map[string]*mock_dockeriface.MockClient)
108+
109+
// Ensure that agent pings all known versions of Docker API
110+
for i := 0; i < len(allVersions); i++ {
111+
mockClients[string(allVersions[i])] = mock_dockeriface.NewMockClient(ctrl)
112+
mockClients[string(allVersions[i])].EXPECT().Version().Return(&docker.Env{"MinAPIVersion=1.12","ApiVersion=1.27"}, nil).AnyTimes()
113+
mockClients[string(allVersions[i])].EXPECT().Ping().AnyTimes()
114+
}
115+
116+
// Define the function for the mock client
117+
// For simplicity, we will pretend all versions of docker are available
118+
newVersionedClient = func(endpoint, version string) (dockeriface.Client, error) {
119+
return mockClients[version], nil
120+
}
121+
122+
factory := NewFactory(expectedEndpoint)
123+
actualVersions := factory.FindSupportedAPIVersions()
124+
125+
assert.Equal(t, len(agentVersions), len(actualVersions))
126+
for i := 0; i < len(actualVersions); i++ {
127+
assert.Equal(t, agentVersions[i], actualVersions[i])
128+
}
129+
}

agent/engine/dockerclient/dockerclientfactory_unix_test.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"github.com/aws/amazon-ecs-agent/agent/engine/dockeriface/mocks"
2222
"github.com/golang/mock/gomock"
2323
"github.com/stretchr/testify/assert"
24+
docker "github.com/fsouza/go-dockerclient"
2425
)
2526

2627
func TestGetClientCached(t *testing.T) {
@@ -29,7 +30,8 @@ func TestGetClientCached(t *testing.T) {
2930

3031
newVersionedClient = func(endpoint, version string) (dockeriface.Client, error) {
3132
mockClient := mock_dockeriface.NewMockClient(ctrl)
32-
mockClient.EXPECT().Ping()
33+
mockClient.EXPECT().Version().Return(&docker.Env{}, nil).AnyTimes()
34+
mockClient.EXPECT().Ping().AnyTimes()
3335
return mockClient, nil
3436
}
3537

agent/engine/dockerclient/dockerclientfactory_windows_test.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"github.com/aws/amazon-ecs-agent/agent/engine/dockeriface/mocks"
2222
"github.com/golang/mock/gomock"
2323
"github.com/stretchr/testify/assert"
24+
docker "github.com/fsouza/go-dockerclient"
2425
)
2526

2627
func TestGetClientMinimumVersion(t *testing.T) {
@@ -34,7 +35,8 @@ func TestGetClientMinimumVersion(t *testing.T) {
3435
if version == string(MinDockerAPIWindows) {
3536
mockClient = expectedClient
3637
}
37-
mockClient.EXPECT().Ping()
38+
mockClient.EXPECT().Version().Return(&docker.Env{}, nil).AnyTimes()
39+
mockClient.EXPECT().Ping().AnyTimes()
3840
return mockClient, nil
3941
}
4042

agent/utils/compare_versions.go

+12
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ import (
2020
"strings"
2121
)
2222

23+
// zeroPatch is a string to append patch number zero if the major minor version lacks it
24+
const zeroPatch = ".0"
25+
2326
type Version string
2427

2528
type semver struct {
@@ -93,6 +96,9 @@ func (lhs Version) Matches(selector string) (bool, error) {
9396

9497
func parseSemver(version string) (semver, error) {
9598
var result semver
99+
if ok := checkForNoPatch(version); ok {
100+
version += zeroPatch
101+
}
96102
// 0.0.0-some-prealpha-stuff.1+12345
97103
versionAndMetadata := strings.SplitN(version, "+", 2)
98104
// [0.0.0-some-prealpha-stuff.1, 12345]
@@ -128,6 +134,12 @@ func parseSemver(version string) (semver, error) {
128134
return result, nil
129135
}
130136

137+
func checkForNoPatch(version string) bool {
138+
versionParts := strings.Split(version, ".")
139+
// Only major and minor versions are present
140+
return len(versionParts) == 2
141+
}
142+
131143
// compareSemver compares two semvers, 'lhs' and 'rhs', and returns -1 if lhs is less
132144
// than rhs, 0 if they are equal, and 1 lhs is greater than rhs
133145
func compareSemver(lhs, rhs semver) int {

agent/utils/compare_versions_test.go

+5
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,11 @@ func TestVersionMatches(t *testing.T) {
185185
selector: ">=1.5.0",
186186
expectedOutput: true,
187187
},
188+
{
189+
version: "1.17",
190+
selector: "<1.12",
191+
expectedOutput: false,
192+
},
188193
}
189194

190195
for i, testCase := range testCases {

0 commit comments

Comments
 (0)