diff --git a/api/client/hijack.go b/api/client/hijack.go index becb8a7479..067d9b50fd 100644 --- a/api/client/hijack.go +++ b/api/client/hijack.go @@ -138,7 +138,7 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.Rea if err != nil { return err } - req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", api.APIVERSION, path), params) + req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", api.Version, path), params) if err != nil { return err } diff --git a/api/client/utils.go b/api/client/utils.go index b9d8f27c44..a00a45d243 100644 --- a/api/client/utils.go +++ b/api/client/utils.go @@ -53,7 +53,7 @@ func (cli *DockerCli) clientRequest(method, path string, in io.Reader, headers m if expectedPayload && in == nil { in = bytes.NewReader([]byte{}) } - req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", api.APIVERSION, path), in) + req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", api.Version, path), in) if err != nil { return nil, nil, -1, err } diff --git a/api/client/version.go b/api/client/version.go index ed641cace1..85991dc9a5 100644 --- a/api/client/version.go +++ b/api/client/version.go @@ -26,7 +26,7 @@ func (cli *DockerCli) CmdVersion(args ...string) error { if dockerversion.VERSION != "" { fmt.Fprintf(cli.out, "Client version: %s\n", dockerversion.VERSION) } - fmt.Fprintf(cli.out, "Client API version: %s\n", api.APIVERSION) + fmt.Fprintf(cli.out, "Client API version: %s\n", api.Version) fmt.Fprintf(cli.out, "Go version (client): %s\n", runtime.Version()) if dockerversion.GITCOMMIT != "" { fmt.Fprintf(cli.out, "Git commit (client): %s\n", dockerversion.GITCOMMIT) diff --git a/api/common.go b/api/common.go index cfe3b6216b..8d65edabe2 100644 --- a/api/common.go +++ b/api/common.go @@ -16,8 +16,14 @@ import ( // Common constants for daemon and client. const ( - APIVERSION version.Version = "1.20" // Current REST API version - DefaultDockerfileName string = "Dockerfile" // Default filename with Docker commands, read by docker build + // Current REST API version + Version version.Version = "1.20" + + // Minimun REST API version supported + MinVersion version.Version = "1.12" + + // Default filename with Docker commands, read by docker build + DefaultDockerfileName string = "Dockerfile" ) type ByPrivatePort []types.Port diff --git a/api/server/server.go b/api/server/server.go index fe7810e997..6e6631fc41 100644 --- a/api/server/server.go +++ b/api/server/server.go @@ -244,7 +244,7 @@ func (s *Server) postAuth(version version.Version, w http.ResponseWriter, r *htt func (s *Server) getVersion(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { v := &types.Version{ Version: dockerversion.VERSION, - ApiVersion: api.APIVERSION, + ApiVersion: api.Version, GitCommit: dockerversion.GITCOMMIT, GoVersion: runtime.Version(), Os: runtime.GOOS, @@ -1477,14 +1477,18 @@ func makeHttpHandler(logging bool, localMethod string, localRoute string, handle } version := version.Version(mux.Vars(r)["version"]) if version == "" { - version = api.APIVERSION + version = api.Version } if corsHeaders != "" { writeCorsHeaders(w, r, corsHeaders) } - if version.GreaterThan(api.APIVERSION) { - http.Error(w, fmt.Errorf("client is newer than server (client API version: %s, server API version: %s)", version, api.APIVERSION).Error(), http.StatusBadRequest) + if version.GreaterThan(api.Version) { + http.Error(w, fmt.Errorf("client is newer than server (client API version: %s, server API version: %s)", version, api.Version).Error(), http.StatusBadRequest) + return + } + if version.LessThan(api.MinVersion) { + http.Error(w, fmt.Errorf("client is too old, minimum supported API version is %s, please upgrade your client to a newer version", api.MinVersion).Error(), http.StatusBadRequest) return } diff --git a/integration-cli/docker_api_test.go b/integration-cli/docker_api_test.go index f1d0c327d6..f3e3b1d954 100644 --- a/integration-cli/docker_api_test.go +++ b/integration-cli/docker_api_test.go @@ -3,15 +3,18 @@ package main import ( "net/http" "net/http/httputil" + "strconv" + "strings" "time" + "github.com/docker/docker/api" "github.com/go-check/check" ) func (s *DockerSuite) TestApiOptionsRoute(c *check.C) { status, _, err := sockRequest("OPTIONS", "/", nil) - c.Assert(status, check.Equals, http.StatusOK) c.Assert(err, check.IsNil) + c.Assert(status, check.Equals, http.StatusOK) } func (s *DockerSuite) TestApiGetEnabledCors(c *check.C) { @@ -26,7 +29,7 @@ func (s *DockerSuite) TestApiGetEnabledCors(c *check.C) { //c.Assert(res.Header.Get("Access-Control-Allow-Headers"), check.Equals, "Origin, X-Requested-With, Content-Type, Accept, X-Registry-Auth") } -func (s *DockerSuite) TestVersionStatusCode(c *check.C) { +func (s *DockerSuite) TestApiVersionStatusCode(c *check.C) { conn, err := sockConn(time.Duration(10 * time.Second)) c.Assert(err, check.IsNil) @@ -40,3 +43,31 @@ func (s *DockerSuite) TestVersionStatusCode(c *check.C) { res, err := client.Do(req) c.Assert(res.StatusCode, check.Equals, http.StatusBadRequest) } + +func (s *DockerSuite) TestApiClientVersionNewerThanServer(c *check.C) { + v := strings.Split(string(api.Version), ".") + vMinInt, err := strconv.Atoi(v[1]) + c.Assert(err, check.IsNil) + vMinInt++ + v[1] = strconv.Itoa(vMinInt) + version := strings.Join(v, ".") + + status, body, err := sockRequest("GET", "/v"+version+"/version", nil) + c.Assert(err, check.IsNil) + c.Assert(status, check.Equals, http.StatusBadRequest) + c.Assert(len(string(body)), check.Not(check.Equals), 0) // Expected not empty body +} + +func (s *DockerSuite) TestApiClientVersionOldNotSupported(c *check.C) { + v := strings.Split(string(api.MinVersion), ".") + vMinInt, err := strconv.Atoi(v[1]) + c.Assert(err, check.IsNil) + vMinInt-- + v[1] = strconv.Itoa(vMinInt) + version := strings.Join(v, ".") + + status, body, err := sockRequest("GET", "/v"+version+"/version", nil) + c.Assert(err, check.IsNil) + c.Assert(status, check.Equals, http.StatusBadRequest) + c.Assert(len(string(body)), check.Not(check.Equals), 0) // Expected not empty body +}