diff --git a/client/client.go b/client/client.go index 8acfb7f490..298852f55a 100644 --- a/client/client.go +++ b/client/client.go @@ -53,7 +53,6 @@ import ( "sync/atomic" "time" - "github.com/docker/docker/api" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/versions" "github.com/docker/go-connections/sockets" @@ -91,6 +90,16 @@ import ( // [Go stdlib]: https://github.com/golang/go/blob/6244b1946bc2101b01955468f1be502dbadd6807/src/net/http/transport.go#L558-L569 const DummyHost = "api.moby.localhost" +// DefaultAPIVersion is the highest REST API version supported by the client. +// If API-version negotiation is enabled (see [WithAPIVersionNegotiation], +// [Client.NegotiateAPIVersion]), the client may downgrade its API version. +// Similarly, the [WithVersion] and [WithVersionFromEnv] allow overriding +// the version. +// +// This version may be lower than the [api.DefaultVersion], which is the default +// (and highest supported) version of the api library module used. +const DefaultAPIVersion = "1.52" + // fallbackAPIVersion is the version to fallback to if API-version negotiation // fails. This version is the highest version of the API before API-version // negotiation was introduced. If negotiation fails (or no API version was @@ -198,7 +207,7 @@ func NewClientWithOpts(ops ...Opt) (*Client, error) { } c := &Client{ host: DefaultDockerHost, - version: api.DefaultVersion, + version: DefaultAPIVersion, client: client, proto: hostURL.Scheme, addr: hostURL.Host, @@ -381,7 +390,7 @@ func (cli *Client) negotiateAPIVersionPing(pingResponse types.Ping) { // if the client is not initialized with a version, start with the latest supported version if cli.version == "" { - cli.version = api.DefaultVersion + cli.version = DefaultAPIVersion } // if server version is lower than the client version, downgrade diff --git a/client/client_test.go b/client/client_test.go index 8958d38a6c..6f2085f5b5 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -11,7 +11,6 @@ import ( "strings" "testing" - "github.com/docker/docker/api" "github.com/docker/docker/api/types" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" @@ -30,7 +29,7 @@ func TestNewClientWithOpsFromEnv(t *testing.T) { { doc: "default api version", envs: map[string]string{}, - expectedVersion: api.DefaultVersion, + expectedVersion: DefaultAPIVersion, }, { doc: "invalid cert path", @@ -44,7 +43,7 @@ func TestNewClientWithOpsFromEnv(t *testing.T) { envs: map[string]string{ "DOCKER_CERT_PATH": "testdata/", }, - expectedVersion: api.DefaultVersion, + expectedVersion: DefaultAPIVersion, }, { doc: "default api version with cert path and tls verify", @@ -52,7 +51,7 @@ func TestNewClientWithOpsFromEnv(t *testing.T) { "DOCKER_CERT_PATH": "testdata/", "DOCKER_TLS_VERIFY": "1", }, - expectedVersion: api.DefaultVersion, + expectedVersion: DefaultAPIVersion, }, { doc: "default api version with cert path and host", @@ -60,7 +59,7 @@ func TestNewClientWithOpsFromEnv(t *testing.T) { "DOCKER_CERT_PATH": "testdata/", "DOCKER_HOST": "https://notaunixsocket", }, - expectedVersion: api.DefaultVersion, + expectedVersion: DefaultAPIVersion, }, { doc: "invalid docker host", @@ -74,7 +73,7 @@ func TestNewClientWithOpsFromEnv(t *testing.T) { envs: map[string]string{ "DOCKER_HOST": "invalid://url", }, - expectedVersion: api.DefaultVersion, + expectedVersion: DefaultAPIVersion, }, { doc: "override api version", @@ -117,17 +116,17 @@ func TestGetAPIPath(t *testing.T) { }{ { path: "/containers/json", - expected: "/v" + api.DefaultVersion + "/containers/json", + expected: "/v" + DefaultAPIVersion + "/containers/json", }, { path: "/containers/json", query: url.Values{}, - expected: "/v" + api.DefaultVersion + "/containers/json", + expected: "/v" + DefaultAPIVersion + "/containers/json", }, { path: "/containers/json", query: url.Values{"s": []string{"c"}}, - expected: "/v" + api.DefaultVersion + "/containers/json?s=c", + expected: "/v" + DefaultAPIVersion + "/containers/json?s=c", }, { version: "1.22", @@ -235,7 +234,7 @@ func TestNewClientWithOpsFromEnvSetsDefaultVersion(t *testing.T) { client, err := NewClientWithOpts(FromEnv) assert.NilError(t, err) - assert.Check(t, is.Equal(client.ClientVersion(), api.DefaultVersion)) + assert.Check(t, is.Equal(client.ClientVersion(), DefaultAPIVersion)) const expected = "1.22" t.Setenv("DOCKER_API_VERSION", expected) @@ -375,8 +374,8 @@ func TestNegotiateAPIVersionAutomatic(t *testing.T) { ) assert.NilError(t, err) - // Client defaults to use api.DefaultVersion before version-negotiation. - expected := api.DefaultVersion + // Client defaults to use DefaultAPIVersion before version-negotiation. + expected := DefaultAPIVersion assert.Check(t, is.Equal(client.ClientVersion(), expected)) // First request should trigger negotiation @@ -423,7 +422,7 @@ func TestCustomAPIVersion(t *testing.T) { }{ { version: "", - expected: api.DefaultVersion, + expected: DefaultAPIVersion, }, { version: "1.0", @@ -435,7 +434,7 @@ func TestCustomAPIVersion(t *testing.T) { }, { version: "v", - expected: api.DefaultVersion, + expected: DefaultAPIVersion, }, { version: "v1.0", diff --git a/client/options.go b/client/options.go index 6f68fc2b89..320989ac60 100644 --- a/client/options.go +++ b/client/options.go @@ -194,6 +194,10 @@ func WithTLSClientConfigFromEnv() Opt { // WithVersion overrides the client version with the specified one. If an empty // version is provided, the value is ignored to allow version negotiation // (see [WithAPIVersionNegotiation]). +// +// WithVersion does not validate if the client supports the given version, +// and callers should verify if the version is in the correct format and +// lower than the maximum supported version as defined by [DefaultAPIVersion]. func WithVersion(version string) Opt { return func(c *Client) error { if v := strings.TrimPrefix(version, "v"); v != "" { @@ -208,6 +212,10 @@ func WithVersion(version string) Opt { // the DOCKER_API_VERSION ([EnvOverrideAPIVersion]) environment variable. // If DOCKER_API_VERSION is not set, or set to an empty value, the version // is not modified. +// +// WithVersion does not validate if the client supports the given version, +// and callers should verify if the version is in the correct format and +// lower than the maximum supported version as defined by [DefaultAPIVersion]. func WithVersionFromEnv() Opt { return func(c *Client) error { return WithVersion(os.Getenv(EnvOverrideAPIVersion))(c) diff --git a/client/options_test.go b/client/options_test.go index 7f4b1a0d9a..c465093355 100644 --- a/client/options_test.go +++ b/client/options_test.go @@ -7,7 +7,6 @@ import ( "testing" "time" - "github.com/docker/docker/api" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" ) @@ -50,7 +49,7 @@ func TestOptionWithVersionFromEnv(t *testing.T) { c, err := NewClientWithOpts(WithVersionFromEnv()) assert.NilError(t, err) assert.Check(t, c.client != nil) - assert.Check(t, is.Equal(c.version, api.DefaultVersion)) + assert.Check(t, is.Equal(c.version, DefaultAPIVersion)) assert.Check(t, is.Equal(c.manualOverride, false)) t.Setenv("DOCKER_API_VERSION", "2.9999")