Browse code

Force API versioning in the client library.

Remove dependencies on docker's version packages.
Allow empty version as a fallback to latest version.

Signed-off-by: David Calavera <david.calavera@gmail.com>

David Calavera authored on 2015/12/15 02:06:42
Showing 3 changed files
... ...
@@ -7,6 +7,7 @@ import (
7 7
 	"os"
8 8
 	"runtime"
9 9
 
10
+	"github.com/docker/docker/api"
10 11
 	"github.com/docker/docker/api/client/lib"
11 12
 	"github.com/docker/docker/cli"
12 13
 	"github.com/docker/docker/cliconfig"
... ...
@@ -102,7 +103,7 @@ func NewDockerCli(in io.ReadCloser, out, err io.Writer, clientFlags *cli.ClientF
102 102
 		}
103 103
 		customHeaders["User-Agent"] = "Docker-Client/" + dockerversion.Version + " (" + runtime.GOOS + ")"
104 104
 
105
-		client, err := lib.NewClient(host, clientFlags.Common.TLSOptions, customHeaders)
105
+		client, err := lib.NewClient(host, string(api.Version), clientFlags.Common.TLSOptions, customHeaders)
106 106
 		if err != nil {
107 107
 			return err
108 108
 		}
... ...
@@ -7,10 +7,8 @@ import (
7 7
 	"net/url"
8 8
 	"strings"
9 9
 
10
-	"github.com/docker/docker/api"
11 10
 	"github.com/docker/docker/pkg/sockets"
12 11
 	"github.com/docker/docker/pkg/tlsconfig"
13
-	"github.com/docker/docker/pkg/version"
14 12
 )
15 13
 
16 14
 // Client is the API client that performs all operations
... ...
@@ -29,24 +27,16 @@ type Client struct {
29 29
 	// httpClient holds the client transport instance. Exported to keep the old code running.
30 30
 	httpClient *http.Client
31 31
 	// version of the server to talk to.
32
-	version version.Version
32
+	version string
33 33
 	// custom http headers configured by users
34 34
 	customHTTPHeaders map[string]string
35 35
 }
36 36
 
37
-// NewClient initializes a new API client
38
-// for the given host. It uses the tlsOptions
39
-// to decide whether to use a secure connection or not.
37
+// NewClient initializes a new API client for the given host and API version.
38
+// It won't send any version information if the version number is empty.
39
+// It uses the tlsOptions to decide whether to use a secure connection or not.
40 40
 // It also initializes the custom http headers to add to each request.
41
-func NewClient(host string, tlsOptions *tlsconfig.Options, httpHeaders map[string]string) (*Client, error) {
42
-	return NewClientWithVersion(host, api.Version, tlsOptions, httpHeaders)
43
-}
44
-
45
-// NewClientWithVersion initializes a new API client
46
-// for the given host and API version. It uses the tlsOptions
47
-// to decide whether to use a secure connection or not.
48
-// It also initializes the custom http headers to add to each request.
49
-func NewClientWithVersion(host string, version version.Version, tlsOptions *tlsconfig.Options, httpHeaders map[string]string) (*Client, error) {
41
+func NewClient(host string, version string, tlsOptions *tlsconfig.Options, httpHeaders map[string]string) (*Client, error) {
50 42
 	var (
51 43
 		basePath       string
52 44
 		tlsConfig      *tls.Config
... ...
@@ -94,7 +84,13 @@ func NewClientWithVersion(host string, version version.Version, tlsOptions *tlsc
94 94
 // getAPIPath returns the versioned request path to call the api.
95 95
 // It appends the query parameters to the path if they are not empty.
96 96
 func (cli *Client) getAPIPath(p string, query url.Values) string {
97
-	apiPath := fmt.Sprintf("%s/v%s%s", cli.basePath, cli.version, p)
97
+	var apiPath string
98
+	if cli.version != "" {
99
+		v := strings.TrimPrefix(cli.version, "v")
100
+		apiPath = fmt.Sprintf("%s/v%s%s", cli.basePath, v, p)
101
+	} else {
102
+		apiPath = fmt.Sprintf("%s%s", cli.basePath, p)
103
+	}
98 104
 	if len(query) > 0 {
99 105
 		apiPath += "?" + query.Encode()
100 106
 	}
101 107
new file mode 100644
... ...
@@ -0,0 +1,36 @@
0
+package lib
1
+
2
+import (
3
+	"net/url"
4
+	"testing"
5
+)
6
+
7
+func TestGetAPIPath(t *testing.T) {
8
+	cases := []struct {
9
+		v string
10
+		p string
11
+		q url.Values
12
+		e string
13
+	}{
14
+		{"", "/containers/json", nil, "/containers/json"},
15
+		{"", "/containers/json", url.Values{}, "/containers/json"},
16
+		{"", "/containers/json", url.Values{"s": []string{"c"}}, "/containers/json?s=c"},
17
+		{"1.22", "/containers/json", nil, "/v1.22/containers/json"},
18
+		{"1.22", "/containers/json", url.Values{}, "/v1.22/containers/json"},
19
+		{"1.22", "/containers/json", url.Values{"s": []string{"c"}}, "/v1.22/containers/json?s=c"},
20
+		{"v1.22", "/containers/json", nil, "/v1.22/containers/json"},
21
+		{"v1.22", "/containers/json", url.Values{}, "/v1.22/containers/json"},
22
+		{"v1.22", "/containers/json", url.Values{"s": []string{"c"}}, "/v1.22/containers/json?s=c"},
23
+	}
24
+
25
+	for _, cs := range cases {
26
+		c, err := NewClient("unix:///var/run/docker.sock", cs.v, nil, nil)
27
+		if err != nil {
28
+			t.Fatal(err)
29
+		}
30
+		g := c.getAPIPath(cs.p, cs.q)
31
+		if g != cs.e {
32
+			t.Fatalf("Expected %s, got %s", cs.e, g)
33
+		}
34
+	}
35
+}