From b679eb9a8263d2cb00fecc8741d62d2b35db5ac5 Mon Sep 17 00:00:00 2001 From: David Calavera Date: Mon, 14 Dec 2015 12:06:42 -0500 Subject: [PATCH] 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 --- api/client/cli.go | 3 ++- api/client/lib/client.go | 28 ++++++++++++--------------- api/client/lib/client_test.go | 36 +++++++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 17 deletions(-) create mode 100644 api/client/lib/client_test.go diff --git a/api/client/cli.go b/api/client/cli.go index 19c635d776..7f600d3489 100644 --- a/api/client/cli.go +++ b/api/client/cli.go @@ -7,6 +7,7 @@ import ( "os" "runtime" + "github.com/docker/docker/api" "github.com/docker/docker/api/client/lib" "github.com/docker/docker/cli" "github.com/docker/docker/cliconfig" @@ -102,7 +103,7 @@ func NewDockerCli(in io.ReadCloser, out, err io.Writer, clientFlags *cli.ClientF } customHeaders["User-Agent"] = "Docker-Client/" + dockerversion.Version + " (" + runtime.GOOS + ")" - client, err := lib.NewClient(host, clientFlags.Common.TLSOptions, customHeaders) + client, err := lib.NewClient(host, string(api.Version), clientFlags.Common.TLSOptions, customHeaders) if err != nil { return err } diff --git a/api/client/lib/client.go b/api/client/lib/client.go index 3625a8170f..f0ad922fa1 100644 --- a/api/client/lib/client.go +++ b/api/client/lib/client.go @@ -7,10 +7,8 @@ import ( "net/url" "strings" - "github.com/docker/docker/api" "github.com/docker/docker/pkg/sockets" "github.com/docker/docker/pkg/tlsconfig" - "github.com/docker/docker/pkg/version" ) // Client is the API client that performs all operations @@ -29,24 +27,16 @@ type Client struct { // httpClient holds the client transport instance. Exported to keep the old code running. httpClient *http.Client // version of the server to talk to. - version version.Version + version string // custom http headers configured by users customHTTPHeaders map[string]string } -// NewClient initializes a new API client -// for the given host. It uses the tlsOptions -// to decide whether to use a secure connection or not. +// NewClient initializes a new API client for the given host and API version. +// It won't send any version information if the version number is empty. +// It uses the tlsOptions to decide whether to use a secure connection or not. // It also initializes the custom http headers to add to each request. -func NewClient(host string, tlsOptions *tlsconfig.Options, httpHeaders map[string]string) (*Client, error) { - return NewClientWithVersion(host, api.Version, tlsOptions, httpHeaders) -} - -// NewClientWithVersion initializes a new API client -// for the given host and API version. It uses the tlsOptions -// to decide whether to use a secure connection or not. -// It also initializes the custom http headers to add to each request. -func NewClientWithVersion(host string, version version.Version, tlsOptions *tlsconfig.Options, httpHeaders map[string]string) (*Client, error) { +func NewClient(host string, version string, tlsOptions *tlsconfig.Options, httpHeaders map[string]string) (*Client, error) { var ( basePath string tlsConfig *tls.Config @@ -94,7 +84,13 @@ func NewClientWithVersion(host string, version version.Version, tlsOptions *tlsc // getAPIPath returns the versioned request path to call the api. // It appends the query parameters to the path if they are not empty. func (cli *Client) getAPIPath(p string, query url.Values) string { - apiPath := fmt.Sprintf("%s/v%s%s", cli.basePath, cli.version, p) + var apiPath string + if cli.version != "" { + v := strings.TrimPrefix(cli.version, "v") + apiPath = fmt.Sprintf("%s/v%s%s", cli.basePath, v, p) + } else { + apiPath = fmt.Sprintf("%s%s", cli.basePath, p) + } if len(query) > 0 { apiPath += "?" + query.Encode() } diff --git a/api/client/lib/client_test.go b/api/client/lib/client_test.go new file mode 100644 index 0000000000..97634beb85 --- /dev/null +++ b/api/client/lib/client_test.go @@ -0,0 +1,36 @@ +package lib + +import ( + "net/url" + "testing" +) + +func TestGetAPIPath(t *testing.T) { + cases := []struct { + v string + p string + q url.Values + e string + }{ + {"", "/containers/json", nil, "/containers/json"}, + {"", "/containers/json", url.Values{}, "/containers/json"}, + {"", "/containers/json", url.Values{"s": []string{"c"}}, "/containers/json?s=c"}, + {"1.22", "/containers/json", nil, "/v1.22/containers/json"}, + {"1.22", "/containers/json", url.Values{}, "/v1.22/containers/json"}, + {"1.22", "/containers/json", url.Values{"s": []string{"c"}}, "/v1.22/containers/json?s=c"}, + {"v1.22", "/containers/json", nil, "/v1.22/containers/json"}, + {"v1.22", "/containers/json", url.Values{}, "/v1.22/containers/json"}, + {"v1.22", "/containers/json", url.Values{"s": []string{"c"}}, "/v1.22/containers/json?s=c"}, + } + + for _, cs := range cases { + c, err := NewClient("unix:///var/run/docker.sock", cs.v, nil, nil) + if err != nil { + t.Fatal(err) + } + g := c.getAPIPath(cs.p, cs.q) + if g != cs.e { + t.Fatalf("Expected %s, got %s", cs.e, g) + } + } +}