client_test.go 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. package client
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "io/ioutil"
  6. "net/http"
  7. "net/url"
  8. "os"
  9. "runtime"
  10. "strings"
  11. "testing"
  12. "github.com/docker/docker/api"
  13. "github.com/docker/docker/api/types"
  14. "github.com/stretchr/testify/assert"
  15. "golang.org/x/net/context"
  16. )
  17. func TestNewEnvClient(t *testing.T) {
  18. if runtime.GOOS == "windows" {
  19. t.Skip("skipping unix only test for windows")
  20. }
  21. cases := []struct {
  22. envs map[string]string
  23. expectedError string
  24. expectedVersion string
  25. }{
  26. {
  27. envs: map[string]string{},
  28. expectedVersion: api.DefaultVersion,
  29. },
  30. {
  31. envs: map[string]string{
  32. "DOCKER_CERT_PATH": "invalid/path",
  33. },
  34. expectedError: "Could not load X509 key pair: open invalid/path/cert.pem: no such file or directory",
  35. },
  36. {
  37. envs: map[string]string{
  38. "DOCKER_CERT_PATH": "testdata/",
  39. },
  40. expectedVersion: api.DefaultVersion,
  41. },
  42. {
  43. envs: map[string]string{
  44. "DOCKER_CERT_PATH": "testdata/",
  45. "DOCKER_TLS_VERIFY": "1",
  46. },
  47. expectedVersion: api.DefaultVersion,
  48. },
  49. {
  50. envs: map[string]string{
  51. "DOCKER_CERT_PATH": "testdata/",
  52. "DOCKER_HOST": "https://notaunixsocket",
  53. },
  54. expectedVersion: api.DefaultVersion,
  55. },
  56. {
  57. envs: map[string]string{
  58. "DOCKER_HOST": "host",
  59. },
  60. expectedError: "unable to parse docker host `host`",
  61. },
  62. {
  63. envs: map[string]string{
  64. "DOCKER_HOST": "invalid://url",
  65. },
  66. expectedVersion: api.DefaultVersion,
  67. },
  68. {
  69. envs: map[string]string{
  70. "DOCKER_API_VERSION": "anything",
  71. },
  72. expectedVersion: "anything",
  73. },
  74. {
  75. envs: map[string]string{
  76. "DOCKER_API_VERSION": "1.22",
  77. },
  78. expectedVersion: "1.22",
  79. },
  80. }
  81. for _, c := range cases {
  82. recoverEnvs := setupEnvs(t, c.envs)
  83. apiclient, err := NewEnvClient()
  84. if c.expectedError != "" {
  85. if err == nil {
  86. t.Errorf("expected an error for %v", c)
  87. } else if err.Error() != c.expectedError {
  88. t.Errorf("expected an error %s, got %s, for %v", c.expectedError, err.Error(), c)
  89. }
  90. } else {
  91. if err != nil {
  92. t.Error(err)
  93. }
  94. version := apiclient.ClientVersion()
  95. if version != c.expectedVersion {
  96. t.Errorf("expected %s, got %s, for %v", c.expectedVersion, version, c)
  97. }
  98. }
  99. if c.envs["DOCKER_TLS_VERIFY"] != "" {
  100. // pedantic checking that this is handled correctly
  101. tr := apiclient.client.Transport.(*http.Transport)
  102. if tr.TLSClientConfig == nil {
  103. t.Error("no TLS config found when DOCKER_TLS_VERIFY enabled")
  104. }
  105. if tr.TLSClientConfig.InsecureSkipVerify {
  106. t.Error("TLS verification should be enabled")
  107. }
  108. }
  109. recoverEnvs(t)
  110. }
  111. }
  112. func setupEnvs(t *testing.T, envs map[string]string) func(*testing.T) {
  113. oldEnvs := map[string]string{}
  114. for key, value := range envs {
  115. oldEnv := os.Getenv(key)
  116. oldEnvs[key] = oldEnv
  117. err := os.Setenv(key, value)
  118. if err != nil {
  119. t.Error(err)
  120. }
  121. }
  122. return func(t *testing.T) {
  123. for key, value := range oldEnvs {
  124. err := os.Setenv(key, value)
  125. if err != nil {
  126. t.Error(err)
  127. }
  128. }
  129. }
  130. }
  131. func TestGetAPIPath(t *testing.T) {
  132. cases := []struct {
  133. v string
  134. p string
  135. q url.Values
  136. e string
  137. }{
  138. {"", "/containers/json", nil, "/containers/json"},
  139. {"", "/containers/json", url.Values{}, "/containers/json"},
  140. {"", "/containers/json", url.Values{"s": []string{"c"}}, "/containers/json?s=c"},
  141. {"1.22", "/containers/json", nil, "/v1.22/containers/json"},
  142. {"1.22", "/containers/json", url.Values{}, "/v1.22/containers/json"},
  143. {"1.22", "/containers/json", url.Values{"s": []string{"c"}}, "/v1.22/containers/json?s=c"},
  144. {"v1.22", "/containers/json", nil, "/v1.22/containers/json"},
  145. {"v1.22", "/containers/json", url.Values{}, "/v1.22/containers/json"},
  146. {"v1.22", "/containers/json", url.Values{"s": []string{"c"}}, "/v1.22/containers/json?s=c"},
  147. {"v1.22", "/networks/kiwl$%^", nil, "/v1.22/networks/kiwl$%25%5E"},
  148. }
  149. for _, cs := range cases {
  150. c, err := NewClient("unix:///var/run/docker.sock", cs.v, nil, nil)
  151. if err != nil {
  152. t.Fatal(err)
  153. }
  154. g := c.getAPIPath(cs.p, cs.q)
  155. if g != cs.e {
  156. t.Fatalf("Expected %s, got %s", cs.e, g)
  157. }
  158. err = c.Close()
  159. if nil != err {
  160. t.Fatalf("close client failed, error message: %s", err)
  161. }
  162. }
  163. }
  164. func TestParseHost(t *testing.T) {
  165. cases := []struct {
  166. host string
  167. proto string
  168. addr string
  169. base string
  170. err bool
  171. }{
  172. {"", "", "", "", true},
  173. {"foobar", "", "", "", true},
  174. {"foo://bar", "foo", "bar", "", false},
  175. {"tcp://localhost:2476", "tcp", "localhost:2476", "", false},
  176. {"tcp://localhost:2476/path", "tcp", "localhost:2476", "/path", false},
  177. }
  178. for _, cs := range cases {
  179. p, a, b, e := ParseHost(cs.host)
  180. if cs.err && e == nil {
  181. t.Fatalf("expected error, got nil")
  182. }
  183. if !cs.err && e != nil {
  184. t.Fatal(e)
  185. }
  186. if cs.proto != p {
  187. t.Fatalf("expected proto %s, got %s", cs.proto, p)
  188. }
  189. if cs.addr != a {
  190. t.Fatalf("expected addr %s, got %s", cs.addr, a)
  191. }
  192. if cs.base != b {
  193. t.Fatalf("expected base %s, got %s", cs.base, b)
  194. }
  195. }
  196. }
  197. func TestUpdateClientVersion(t *testing.T) {
  198. client := &Client{
  199. client: newMockClient(func(req *http.Request) (*http.Response, error) {
  200. splitQuery := strings.Split(req.URL.Path, "/")
  201. queryVersion := splitQuery[1]
  202. b, err := json.Marshal(types.Version{
  203. APIVersion: queryVersion,
  204. })
  205. if err != nil {
  206. return nil, err
  207. }
  208. return &http.Response{
  209. StatusCode: http.StatusOK,
  210. Body: ioutil.NopCloser(bytes.NewReader(b)),
  211. }, nil
  212. }),
  213. }
  214. cases := []struct {
  215. v string
  216. }{
  217. {"1.20"},
  218. {"v1.21"},
  219. {"1.22"},
  220. {"v1.22"},
  221. }
  222. for _, cs := range cases {
  223. client.UpdateClientVersion(cs.v)
  224. r, err := client.ServerVersion(context.Background())
  225. if err != nil {
  226. t.Fatal(err)
  227. }
  228. if strings.TrimPrefix(r.APIVersion, "v") != strings.TrimPrefix(cs.v, "v") {
  229. t.Fatalf("Expected %s, got %s", cs.v, r.APIVersion)
  230. }
  231. }
  232. }
  233. func TestNewEnvClientSetsDefaultVersion(t *testing.T) {
  234. // Unset environment variables
  235. envVarKeys := []string{
  236. "DOCKER_HOST",
  237. "DOCKER_API_VERSION",
  238. "DOCKER_TLS_VERIFY",
  239. "DOCKER_CERT_PATH",
  240. }
  241. envVarValues := make(map[string]string)
  242. for _, key := range envVarKeys {
  243. envVarValues[key] = os.Getenv(key)
  244. os.Setenv(key, "")
  245. }
  246. client, err := NewEnvClient()
  247. if err != nil {
  248. t.Fatal(err)
  249. }
  250. if client.version != api.DefaultVersion {
  251. t.Fatalf("Expected %s, got %s", api.DefaultVersion, client.version)
  252. }
  253. expected := "1.22"
  254. os.Setenv("DOCKER_API_VERSION", expected)
  255. client, err = NewEnvClient()
  256. if err != nil {
  257. t.Fatal(err)
  258. }
  259. if client.version != expected {
  260. t.Fatalf("Expected %s, got %s", expected, client.version)
  261. }
  262. // Restore environment variables
  263. for _, key := range envVarKeys {
  264. os.Setenv(key, envVarValues[key])
  265. }
  266. }
  267. type roundTripFunc func(*http.Request) (*http.Response, error)
  268. func (rtf roundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) {
  269. return rtf(req)
  270. }
  271. type bytesBufferClose struct {
  272. *bytes.Buffer
  273. }
  274. func (bbc bytesBufferClose) Close() error {
  275. return nil
  276. }
  277. func TestClientRedirect(t *testing.T) {
  278. client := &http.Client{
  279. CheckRedirect: CheckRedirect,
  280. Transport: roundTripFunc(func(req *http.Request) (*http.Response, error) {
  281. if req.URL.String() == "/bla" {
  282. return &http.Response{StatusCode: 404}, nil
  283. }
  284. return &http.Response{
  285. StatusCode: 301,
  286. Header: map[string][]string{"Location": {"/bla"}},
  287. Body: bytesBufferClose{bytes.NewBuffer(nil)},
  288. }, nil
  289. }),
  290. }
  291. cases := []struct {
  292. httpMethod string
  293. expectedErr error
  294. statusCode int
  295. }{
  296. {http.MethodGet, nil, 301},
  297. {http.MethodPost, &url.Error{Op: "Post", URL: "/bla", Err: ErrRedirect}, 301},
  298. {http.MethodPut, &url.Error{Op: "Put", URL: "/bla", Err: ErrRedirect}, 301},
  299. {http.MethodDelete, &url.Error{Op: "Delete", URL: "/bla", Err: ErrRedirect}, 301},
  300. }
  301. for _, tc := range cases {
  302. req, err := http.NewRequest(tc.httpMethod, "/redirectme", nil)
  303. assert.NoError(t, err)
  304. resp, err := client.Do(req)
  305. assert.Equal(t, tc.expectedErr, err)
  306. assert.Equal(t, tc.statusCode, resp.StatusCode)
  307. }
  308. }