123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334 |
- package client
- import (
- "bytes"
- "encoding/json"
- "io/ioutil"
- "net/http"
- "net/url"
- "os"
- "runtime"
- "strings"
- "testing"
- "github.com/docker/docker/api"
- "github.com/docker/docker/api/types"
- "github.com/stretchr/testify/assert"
- "golang.org/x/net/context"
- )
- func TestNewEnvClient(t *testing.T) {
- if runtime.GOOS == "windows" {
- t.Skip("skipping unix only test for windows")
- }
- cases := []struct {
- envs map[string]string
- expectedError string
- expectedVersion string
- }{
- {
- envs: map[string]string{},
- expectedVersion: api.DefaultVersion,
- },
- {
- envs: map[string]string{
- "DOCKER_CERT_PATH": "invalid/path",
- },
- expectedError: "Could not load X509 key pair: open invalid/path/cert.pem: no such file or directory",
- },
- {
- envs: map[string]string{
- "DOCKER_CERT_PATH": "testdata/",
- },
- expectedVersion: api.DefaultVersion,
- },
- {
- envs: map[string]string{
- "DOCKER_CERT_PATH": "testdata/",
- "DOCKER_TLS_VERIFY": "1",
- },
- expectedVersion: api.DefaultVersion,
- },
- {
- envs: map[string]string{
- "DOCKER_CERT_PATH": "testdata/",
- "DOCKER_HOST": "https://notaunixsocket",
- },
- expectedVersion: api.DefaultVersion,
- },
- {
- envs: map[string]string{
- "DOCKER_HOST": "host",
- },
- expectedError: "unable to parse docker host `host`",
- },
- {
- envs: map[string]string{
- "DOCKER_HOST": "invalid://url",
- },
- expectedVersion: api.DefaultVersion,
- },
- {
- envs: map[string]string{
- "DOCKER_API_VERSION": "anything",
- },
- expectedVersion: "anything",
- },
- {
- envs: map[string]string{
- "DOCKER_API_VERSION": "1.22",
- },
- expectedVersion: "1.22",
- },
- }
- for _, c := range cases {
- recoverEnvs := setupEnvs(t, c.envs)
- apiclient, err := NewEnvClient()
- if c.expectedError != "" {
- if err == nil {
- t.Errorf("expected an error for %v", c)
- } else if err.Error() != c.expectedError {
- t.Errorf("expected an error %s, got %s, for %v", c.expectedError, err.Error(), c)
- }
- } else {
- if err != nil {
- t.Error(err)
- }
- version := apiclient.ClientVersion()
- if version != c.expectedVersion {
- t.Errorf("expected %s, got %s, for %v", c.expectedVersion, version, c)
- }
- }
- if c.envs["DOCKER_TLS_VERIFY"] != "" {
- // pedantic checking that this is handled correctly
- tr := apiclient.client.Transport.(*http.Transport)
- if tr.TLSClientConfig == nil {
- t.Error("no TLS config found when DOCKER_TLS_VERIFY enabled")
- }
- if tr.TLSClientConfig.InsecureSkipVerify {
- t.Error("TLS verification should be enabled")
- }
- }
- recoverEnvs(t)
- }
- }
- func setupEnvs(t *testing.T, envs map[string]string) func(*testing.T) {
- oldEnvs := map[string]string{}
- for key, value := range envs {
- oldEnv := os.Getenv(key)
- oldEnvs[key] = oldEnv
- err := os.Setenv(key, value)
- if err != nil {
- t.Error(err)
- }
- }
- return func(t *testing.T) {
- for key, value := range oldEnvs {
- err := os.Setenv(key, value)
- if err != nil {
- t.Error(err)
- }
- }
- }
- }
- 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"},
- {"v1.22", "/networks/kiwl$%^", nil, "/v1.22/networks/kiwl$%25%5E"},
- }
- 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)
- }
- err = c.Close()
- if nil != err {
- t.Fatalf("close client failed, error message: %s", err)
- }
- }
- }
- func TestParseHost(t *testing.T) {
- cases := []struct {
- host string
- proto string
- addr string
- base string
- err bool
- }{
- {"", "", "", "", true},
- {"foobar", "", "", "", true},
- {"foo://bar", "foo", "bar", "", false},
- {"tcp://localhost:2476", "tcp", "localhost:2476", "", false},
- {"tcp://localhost:2476/path", "tcp", "localhost:2476", "/path", false},
- }
- for _, cs := range cases {
- p, a, b, e := ParseHost(cs.host)
- if cs.err && e == nil {
- t.Fatalf("expected error, got nil")
- }
- if !cs.err && e != nil {
- t.Fatal(e)
- }
- if cs.proto != p {
- t.Fatalf("expected proto %s, got %s", cs.proto, p)
- }
- if cs.addr != a {
- t.Fatalf("expected addr %s, got %s", cs.addr, a)
- }
- if cs.base != b {
- t.Fatalf("expected base %s, got %s", cs.base, b)
- }
- }
- }
- func TestUpdateClientVersion(t *testing.T) {
- client := &Client{
- client: newMockClient(func(req *http.Request) (*http.Response, error) {
- splitQuery := strings.Split(req.URL.Path, "/")
- queryVersion := splitQuery[1]
- b, err := json.Marshal(types.Version{
- APIVersion: queryVersion,
- })
- if err != nil {
- return nil, err
- }
- return &http.Response{
- StatusCode: http.StatusOK,
- Body: ioutil.NopCloser(bytes.NewReader(b)),
- }, nil
- }),
- }
- cases := []struct {
- v string
- }{
- {"1.20"},
- {"v1.21"},
- {"1.22"},
- {"v1.22"},
- }
- for _, cs := range cases {
- client.UpdateClientVersion(cs.v)
- r, err := client.ServerVersion(context.Background())
- if err != nil {
- t.Fatal(err)
- }
- if strings.TrimPrefix(r.APIVersion, "v") != strings.TrimPrefix(cs.v, "v") {
- t.Fatalf("Expected %s, got %s", cs.v, r.APIVersion)
- }
- }
- }
- func TestNewEnvClientSetsDefaultVersion(t *testing.T) {
- // Unset environment variables
- envVarKeys := []string{
- "DOCKER_HOST",
- "DOCKER_API_VERSION",
- "DOCKER_TLS_VERIFY",
- "DOCKER_CERT_PATH",
- }
- envVarValues := make(map[string]string)
- for _, key := range envVarKeys {
- envVarValues[key] = os.Getenv(key)
- os.Setenv(key, "")
- }
- client, err := NewEnvClient()
- if err != nil {
- t.Fatal(err)
- }
- if client.version != api.DefaultVersion {
- t.Fatalf("Expected %s, got %s", api.DefaultVersion, client.version)
- }
- expected := "1.22"
- os.Setenv("DOCKER_API_VERSION", expected)
- client, err = NewEnvClient()
- if err != nil {
- t.Fatal(err)
- }
- if client.version != expected {
- t.Fatalf("Expected %s, got %s", expected, client.version)
- }
- // Restore environment variables
- for _, key := range envVarKeys {
- os.Setenv(key, envVarValues[key])
- }
- }
- type roundTripFunc func(*http.Request) (*http.Response, error)
- func (rtf roundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) {
- return rtf(req)
- }
- type bytesBufferClose struct {
- *bytes.Buffer
- }
- func (bbc bytesBufferClose) Close() error {
- return nil
- }
- func TestClientRedirect(t *testing.T) {
- client := &http.Client{
- CheckRedirect: CheckRedirect,
- Transport: roundTripFunc(func(req *http.Request) (*http.Response, error) {
- if req.URL.String() == "/bla" {
- return &http.Response{StatusCode: 404}, nil
- }
- return &http.Response{
- StatusCode: 301,
- Header: map[string][]string{"Location": {"/bla"}},
- Body: bytesBufferClose{bytes.NewBuffer(nil)},
- }, nil
- }),
- }
- cases := []struct {
- httpMethod string
- expectedErr error
- statusCode int
- }{
- {http.MethodGet, nil, 301},
- {http.MethodPost, &url.Error{Op: "Post", URL: "/bla", Err: ErrRedirect}, 301},
- {http.MethodPut, &url.Error{Op: "Put", URL: "/bla", Err: ErrRedirect}, 301},
- {http.MethodDelete, &url.Error{Op: "Delete", URL: "/bla", Err: ErrRedirect}, 301},
- }
- for _, tc := range cases {
- req, err := http.NewRequest(tc.httpMethod, "/redirectme", nil)
- assert.NoError(t, err)
- resp, err := client.Do(req)
- assert.Equal(t, tc.expectedErr, err)
- assert.Equal(t, tc.statusCode, resp.StatusCode)
- }
- }
|