e2e_test.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472
  1. /*
  2. Copyright 2020 Docker, Inc.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package main
  14. import (
  15. "fmt"
  16. "io/ioutil"
  17. "os"
  18. "path/filepath"
  19. "runtime"
  20. "strings"
  21. "testing"
  22. "time"
  23. "gotest.tools/v3/assert"
  24. "gotest.tools/v3/golden"
  25. "gotest.tools/v3/icmd"
  26. . "github.com/docker/api/tests/framework"
  27. )
  28. var binDir string
  29. func TestMain(m *testing.M) {
  30. p, cleanup, err := SetupExistingCLI()
  31. if err != nil {
  32. fmt.Println(err)
  33. os.Exit(1)
  34. }
  35. binDir = p
  36. exitCode := m.Run()
  37. cleanup()
  38. os.Exit(exitCode)
  39. }
  40. func TestComposeNotImplemented(t *testing.T) {
  41. c := NewParallelE2eCLI(t, binDir)
  42. res := c.RunDockerCmd("context", "show")
  43. res.Assert(t, icmd.Expected{Out: "default"})
  44. res = c.RunDockerCmd("compose", "up")
  45. res.Assert(t, icmd.Expected{
  46. ExitCode: 1,
  47. Err: `Command "compose up" not available in current context (default)`,
  48. })
  49. }
  50. func TestContextDefault(t *testing.T) {
  51. c := NewParallelE2eCLI(t, binDir)
  52. t.Run("show", func(t *testing.T) {
  53. t.Parallel()
  54. res := c.RunDockerCmd("context", "show")
  55. res.Assert(t, icmd.Expected{Out: "default"})
  56. })
  57. t.Run("ls", func(t *testing.T) {
  58. t.Parallel()
  59. res := c.RunDockerCmd("context", "ls")
  60. res.Assert(t, icmd.Success)
  61. golden.Assert(t, res.Stdout(), GoldenFile("ls-out-default"))
  62. })
  63. t.Run("inspect", func(t *testing.T) {
  64. t.Parallel()
  65. res := c.RunDockerCmd("context", "inspect", "default")
  66. res.Assert(t, icmd.Expected{Out: `"Name": "default"`})
  67. })
  68. t.Run("inspect current", func(t *testing.T) {
  69. t.Parallel()
  70. res := c.RunDockerCmd("context", "inspect")
  71. res.Assert(t, icmd.Expected{Out: `"Name": "default"`})
  72. })
  73. }
  74. func TestContextCreateDocker(t *testing.T) {
  75. c := NewParallelE2eCLI(t, binDir)
  76. res := c.RunDockerCmd("context", "create", "test-docker", "--from", "default")
  77. res.Assert(t, icmd.Expected{Out: "test-docker"})
  78. t.Run("ls", func(t *testing.T) {
  79. t.Parallel()
  80. res := c.RunDockerCmd("context", "ls")
  81. res.Assert(t, icmd.Success)
  82. golden.Assert(t, res.Stdout(), GoldenFile("ls-out-test-docker"))
  83. })
  84. t.Run("ls quiet", func(t *testing.T) {
  85. t.Parallel()
  86. res := c.RunDockerCmd("context", "ls", "-q")
  87. golden.Assert(t, res.Stdout(), "ls-out-test-docker-quiet.golden")
  88. })
  89. t.Run("ls format", func(t *testing.T) {
  90. t.Parallel()
  91. res := c.RunDockerCmd("context", "ls", "--format", "{{ json . }}")
  92. res.Assert(t, icmd.Expected{Out: `"Name":"default"`})
  93. })
  94. }
  95. func TestContextInspect(t *testing.T) {
  96. c := NewParallelE2eCLI(t, binDir)
  97. res := c.RunDockerCmd("context", "create", "test-docker", "--from", "default")
  98. res.Assert(t, icmd.Expected{Out: "test-docker"})
  99. t.Run("inspect current", func(t *testing.T) {
  100. // Cannot be run in parallel because of "context use"
  101. res := c.RunDockerCmd("context", "use", "test-docker")
  102. res.Assert(t, icmd.Expected{Out: "test-docker"})
  103. res = c.RunDockerCmd("context", "inspect")
  104. res.Assert(t, icmd.Expected{Out: `"Name": "test-docker"`})
  105. })
  106. }
  107. func TestContextHelpACI(t *testing.T) {
  108. c := NewParallelE2eCLI(t, binDir)
  109. t.Run("help", func(t *testing.T) {
  110. t.Parallel()
  111. res := c.RunDockerCmd("context", "create", "aci", "--help")
  112. // Can't use golden here as the help prints the config directory which changes
  113. res.Assert(t, icmd.Expected{Out: "docker context create aci CONTEXT [flags]"})
  114. res.Assert(t, icmd.Expected{Out: "--location"})
  115. res.Assert(t, icmd.Expected{Out: "--subscription-id"})
  116. res.Assert(t, icmd.Expected{Out: "--resource-group"})
  117. })
  118. t.Run("check exec", func(t *testing.T) {
  119. t.Parallel()
  120. res := c.RunDockerCmd("context", "create", "aci", "--subscription-id", "invalid-id")
  121. res.Assert(t, icmd.Expected{
  122. ExitCode: 1,
  123. Err: "accepts 1 arg(s), received 0",
  124. })
  125. assert.Assert(t, !strings.Contains(res.Combined(), "unknown flag"))
  126. })
  127. }
  128. func TestContextDuplicateACI(t *testing.T) {
  129. c := NewParallelE2eCLI(t, binDir)
  130. c.RunDockerCmd("context", "create", "mycontext", "--from", "default").Assert(t, icmd.Success)
  131. res := c.RunDockerCmd("context", "create", "aci", "mycontext")
  132. res.Assert(t, icmd.Expected{
  133. ExitCode: 1,
  134. Err: "context mycontext: already exists",
  135. })
  136. }
  137. func TestContextRemove(t *testing.T) {
  138. t.Run("remove current", func(t *testing.T) {
  139. c := NewParallelE2eCLI(t, binDir)
  140. c.RunDockerCmd("context", "create", "test-context-rm", "--from", "default").Assert(t, icmd.Success)
  141. res := c.RunDockerCmd("context", "use", "test-context-rm")
  142. res.Assert(t, icmd.Expected{Out: "test-context-rm"})
  143. res = c.RunDockerCmd("context", "rm", "test-context-rm")
  144. res.Assert(t, icmd.Expected{
  145. ExitCode: 1,
  146. Err: "cannot delete current context",
  147. })
  148. })
  149. t.Run("force remove current", func(t *testing.T) {
  150. c := NewParallelE2eCLI(t, binDir)
  151. c.RunDockerCmd("context", "create", "test-context-rmf").Assert(t, icmd.Success)
  152. c.RunDockerCmd("context", "use", "test-context-rmf").Assert(t, icmd.Success)
  153. res := c.RunDockerCmd("context", "rm", "-f", "test-context-rmf")
  154. res.Assert(t, icmd.Expected{Out: "test-context-rmf"})
  155. res = c.RunDockerCmd("context", "ls")
  156. res.Assert(t, icmd.Expected{Out: "default *"})
  157. })
  158. }
  159. func TestLoginCommandDelegation(t *testing.T) {
  160. // These tests just check that the existing CLI is called in various cases.
  161. // They do not test actual login functionality.
  162. c := NewParallelE2eCLI(t, binDir)
  163. t.Run("default context", func(t *testing.T) {
  164. t.Parallel()
  165. res := c.RunDockerCmd("login", "-u", "nouser", "-p", "wrongpasword")
  166. res.Assert(t, icmd.Expected{
  167. ExitCode: 1,
  168. Err: "unauthorized: incorrect username or password",
  169. })
  170. })
  171. t.Run("interactive", func(t *testing.T) {
  172. t.Parallel()
  173. res := c.RunDockerCmd("login", "someregistry.docker.io")
  174. res.Assert(t, icmd.Expected{
  175. ExitCode: 1,
  176. Err: "Cannot perform an interactive login from a non TTY device",
  177. })
  178. })
  179. t.Run("logout", func(t *testing.T) {
  180. t.Parallel()
  181. res := c.RunDockerCmd("logout", "someregistry.docker.io")
  182. res.Assert(t, icmd.Expected{Out: "someregistry.docker.io"})
  183. })
  184. t.Run("existing context", func(t *testing.T) {
  185. c := NewParallelE2eCLI(t, binDir)
  186. c.RunDockerCmd("context", "create", "local", "local").Assert(t, icmd.Success)
  187. c.RunDockerCmd("context", "use", "local").Assert(t, icmd.Success)
  188. res := c.RunDockerCmd("login", "-u", "nouser", "-p", "wrongpasword")
  189. res.Assert(t, icmd.Expected{
  190. ExitCode: 1,
  191. Err: "unauthorized: incorrect username or password",
  192. })
  193. })
  194. }
  195. func TestCloudLogin(t *testing.T) {
  196. c := NewParallelE2eCLI(t, binDir)
  197. t.Run("unknown backend", func(t *testing.T) {
  198. t.Parallel()
  199. res := c.RunDockerCmd("login", "mycloudbackend")
  200. res.Assert(t, icmd.Expected{
  201. ExitCode: 1,
  202. Err: "unknown backend type for cloud login: mycloudbackend",
  203. })
  204. })
  205. }
  206. func TestMissingExistingCLI(t *testing.T) {
  207. t.Parallel()
  208. home, err := ioutil.TempDir("", "")
  209. assert.NilError(t, err)
  210. t.Cleanup(func() {
  211. _ = os.RemoveAll(home)
  212. })
  213. bin, err := ioutil.TempDir("", "")
  214. assert.NilError(t, err)
  215. t.Cleanup(func() {
  216. _ = os.RemoveAll(bin)
  217. })
  218. err = CopyFile(filepath.Join(binDir, DockerExecutableName), filepath.Join(bin, DockerExecutableName))
  219. assert.NilError(t, err)
  220. env := []string{"PATH=" + bin}
  221. if runtime.GOOS == "windows" {
  222. env = append(env, "USERPROFILE="+home)
  223. } else {
  224. env = append(env, "HOME="+home)
  225. }
  226. c := icmd.Cmd{
  227. Env: env,
  228. Command: []string{filepath.Join(bin, "docker")},
  229. }
  230. res := icmd.RunCmd(c)
  231. res.Assert(t, icmd.Expected{
  232. ExitCode: 1,
  233. Err: `"com.docker.cli": executable file not found`,
  234. })
  235. }
  236. func TestLegacy(t *testing.T) {
  237. c := NewParallelE2eCLI(t, binDir)
  238. t.Run("help", func(t *testing.T) {
  239. t.Parallel()
  240. res := c.RunDockerCmd("--help")
  241. res.Assert(t, icmd.Expected{Out: "swarm"})
  242. })
  243. t.Run("swarm", func(t *testing.T) {
  244. t.Parallel()
  245. res := c.RunDockerCmd("swarm", "join")
  246. res.Assert(t, icmd.Expected{
  247. ExitCode: 1,
  248. Err: `"docker swarm join" requires exactly 1 argument.`,
  249. })
  250. })
  251. t.Run("local run", func(t *testing.T) {
  252. t.Parallel()
  253. cmd := c.NewDockerCmd("run", "--rm", "hello-world")
  254. cmd.Timeout = 40 * time.Second
  255. res := icmd.RunCmd(cmd)
  256. res.Assert(t, icmd.Expected{Out: "Hello from Docker!"})
  257. })
  258. t.Run("error messages", func(t *testing.T) {
  259. t.Parallel()
  260. res := c.RunDockerCmd("foo")
  261. res.Assert(t, icmd.Expected{
  262. ExitCode: 1,
  263. Err: "docker: 'foo' is not a docker command.",
  264. })
  265. })
  266. t.Run("host flag", func(t *testing.T) {
  267. t.Parallel()
  268. stderr := "Cannot connect to the Docker daemon at tcp://localhost:123"
  269. if runtime.GOOS == "windows" {
  270. stderr = "error during connect: Get http://localhost:123"
  271. }
  272. res := c.RunDockerCmd("-H", "tcp://localhost:123", "version")
  273. res.Assert(t, icmd.Expected{
  274. ExitCode: 1,
  275. Err: stderr,
  276. })
  277. })
  278. t.Run("existing contexts delegate", func(t *testing.T) {
  279. c := NewParallelE2eCLI(t, binDir)
  280. c.RunDockerCmd("context", "create", "moby-ctx", "--from=default").Assert(t, icmd.Success)
  281. c.RunDockerCmd("context", "use", "moby-ctx").Assert(t, icmd.Success)
  282. res := c.RunDockerCmd("swarm", "join")
  283. res.Assert(t, icmd.Expected{
  284. ExitCode: 1,
  285. Err: `"docker swarm join" requires exactly 1 argument.`,
  286. })
  287. })
  288. t.Run("host flag overrides context", func(t *testing.T) {
  289. c := NewParallelE2eCLI(t, binDir)
  290. c.RunDockerCmd("context", "create", "example", "test-example").Assert(t, icmd.Success)
  291. c.RunDockerCmd("context", "use", "test-example").Assert(t, icmd.Success)
  292. endpoint := "unix:///var/run/docker.sock"
  293. if runtime.GOOS == "windows" {
  294. endpoint = "npipe:////./pipe/docker_engine"
  295. }
  296. res := c.RunDockerCmd("-H", endpoint, "ps")
  297. res.Assert(t, icmd.Success)
  298. // Example backend's ps output includes these strings
  299. assert.Assert(t, !strings.Contains(res.Stdout(), "id"))
  300. assert.Assert(t, !strings.Contains(res.Stdout(), "1234"))
  301. })
  302. }
  303. func TestLegacyLogin(t *testing.T) {
  304. c := NewParallelE2eCLI(t, binDir)
  305. t.Run("host flag login", func(t *testing.T) {
  306. t.Parallel()
  307. res := c.RunDockerCmd("-H", "tcp://localhost:123", "login", "-u", "nouser", "-p", "wrongpasword")
  308. res.Assert(t, icmd.Expected{
  309. ExitCode: 1,
  310. Err: "WARNING! Using --password via the CLI is insecure. Use --password-stdin.",
  311. })
  312. })
  313. t.Run("log level flag login", func(t *testing.T) {
  314. t.Parallel()
  315. res := c.RunDockerCmd("--log-level", "debug", "login", "-u", "nouser", "-p", "wrongpasword")
  316. res.Assert(t, icmd.Expected{
  317. ExitCode: 1,
  318. Err: "WARNING! Using --password via the CLI is insecure",
  319. })
  320. })
  321. t.Run("login help global flags", func(t *testing.T) {
  322. t.Parallel()
  323. res := c.RunDockerCmd("login", "--help")
  324. res.Assert(t, icmd.Success)
  325. assert.Assert(t, !strings.Contains(res.Combined(), "--log-level"))
  326. })
  327. }
  328. func TestUnsupportedCommand(t *testing.T) {
  329. c := NewParallelE2eCLI(t, binDir)
  330. res := c.RunDockerCmd("context", "create", "example", "test-example")
  331. res.Assert(t, icmd.Success)
  332. res = c.RunDockerCmd("--context", "test-example", "images")
  333. res.Assert(t, icmd.Expected{
  334. ExitCode: 1,
  335. Err: `Command "images" not available in current context (test-example), you can use the "default" context to run this command`,
  336. })
  337. }
  338. func TestVersion(t *testing.T) {
  339. c := NewParallelE2eCLI(t, binDir)
  340. t.Run("azure version", func(t *testing.T) {
  341. t.Parallel()
  342. res := c.RunDockerCmd("version")
  343. res.Assert(t, icmd.Expected{Out: "Azure integration"})
  344. })
  345. t.Run("format", func(t *testing.T) {
  346. t.Parallel()
  347. res := c.RunDockerCmd("version", "-f", "{{ json . }}")
  348. res.Assert(t, icmd.Expected{Out: `"Client":`})
  349. res = c.RunDockerCmd("version", "--format", "{{ json . }}")
  350. res.Assert(t, icmd.Expected{Out: `"Client":`})
  351. })
  352. t.Run("delegate version flag", func(t *testing.T) {
  353. c := NewParallelE2eCLI(t, binDir)
  354. c.RunDockerCmd("context", "create", "example", "test-example").Assert(t, icmd.Success)
  355. c.RunDockerCmd("context", "use", "test-example").Assert(t, icmd.Success)
  356. res := c.RunDockerCmd("-v")
  357. res.Assert(t, icmd.Expected{Out: "Docker version"})
  358. })
  359. }
  360. func TestMockBackend(t *testing.T) {
  361. c := NewParallelE2eCLI(t, binDir)
  362. c.RunDockerCmd("context", "create", "example", "test-example").Assert(t, icmd.Success)
  363. res := c.RunDockerCmd("context", "use", "test-example")
  364. res.Assert(t, icmd.Expected{Out: "test-example"})
  365. t.Run("use", func(t *testing.T) {
  366. t.Parallel()
  367. res := c.RunDockerCmd("context", "show")
  368. res.Assert(t, icmd.Expected{Out: "test-example"})
  369. res = c.RunDockerCmd("context", "ls")
  370. golden.Assert(t, res.Stdout(), GoldenFile("ls-out-test-example"))
  371. })
  372. t.Run("ps", func(t *testing.T) {
  373. t.Parallel()
  374. res := c.RunDockerCmd("ps")
  375. res.Assert(t, icmd.Success)
  376. golden.Assert(t, res.Stdout(), "ps-out-example.golden")
  377. })
  378. t.Run("ps quiet", func(t *testing.T) {
  379. t.Parallel()
  380. res := c.RunDockerCmd("ps", "-q")
  381. res.Assert(t, icmd.Success)
  382. golden.Assert(t, res.Stdout(), "ps-quiet-out-example.golden")
  383. })
  384. t.Run("ps quiet all", func(t *testing.T) {
  385. t.Parallel()
  386. res := c.RunDockerCmd("ps", "-q", "--all")
  387. res.Assert(t, icmd.Success)
  388. golden.Assert(t, res.Stdout(), "ps-quiet-all-out-example.golden")
  389. })
  390. t.Run("inspect", func(t *testing.T) {
  391. t.Parallel()
  392. res := c.RunDockerCmd("inspect", "id")
  393. res.Assert(t, icmd.Success)
  394. golden.Assert(t, res.Stdout(), "inspect-id.golden")
  395. })
  396. t.Run("run", func(t *testing.T) {
  397. t.Parallel()
  398. res := c.RunDockerCmd("run", "-d", "nginx", "-p", "80:80")
  399. res.Assert(t, icmd.Expected{
  400. Out: `Running container "nginx" with name`,
  401. })
  402. })
  403. }