dispatchers_test.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517
  1. package dockerfile
  2. import (
  3. "fmt"
  4. "runtime"
  5. "strings"
  6. "testing"
  7. "github.com/docker/docker/api/types"
  8. "github.com/docker/docker/api/types/container"
  9. "github.com/docker/docker/api/types/strslice"
  10. "github.com/docker/go-connections/nat"
  11. )
  12. type commandWithFunction struct {
  13. name string
  14. function func(args []string) error
  15. }
  16. func TestCommandsExactlyOneArgument(t *testing.T) {
  17. commands := []commandWithFunction{
  18. {"MAINTAINER", func(args []string) error { return maintainer(nil, args, nil, "") }},
  19. {"FROM", func(args []string) error { return from(nil, args, nil, "") }},
  20. {"WORKDIR", func(args []string) error { return workdir(nil, args, nil, "") }},
  21. {"USER", func(args []string) error { return user(nil, args, nil, "") }},
  22. {"STOPSIGNAL", func(args []string) error { return stopSignal(nil, args, nil, "") }}}
  23. for _, command := range commands {
  24. err := command.function([]string{})
  25. if err == nil {
  26. t.Fatalf("Error should be present for %s command", command.name)
  27. }
  28. expectedError := errExactlyOneArgument(command.name)
  29. if err.Error() != expectedError.Error() {
  30. t.Fatalf("Wrong error message for %s. Got: %s. Should be: %s", command.name, err.Error(), expectedError)
  31. }
  32. }
  33. }
  34. func TestCommandsAtLeastOneArgument(t *testing.T) {
  35. commands := []commandWithFunction{
  36. {"ENV", func(args []string) error { return env(nil, args, nil, "") }},
  37. {"LABEL", func(args []string) error { return label(nil, args, nil, "") }},
  38. {"ONBUILD", func(args []string) error { return onbuild(nil, args, nil, "") }},
  39. {"HEALTHCHECK", func(args []string) error { return healthcheck(nil, args, nil, "") }},
  40. {"EXPOSE", func(args []string) error { return expose(nil, args, nil, "") }},
  41. {"VOLUME", func(args []string) error { return volume(nil, args, nil, "") }}}
  42. for _, command := range commands {
  43. err := command.function([]string{})
  44. if err == nil {
  45. t.Fatalf("Error should be present for %s command", command.name)
  46. }
  47. expectedError := errAtLeastOneArgument(command.name)
  48. if err.Error() != expectedError.Error() {
  49. t.Fatalf("Wrong error message for %s. Got: %s. Should be: %s", command.name, err.Error(), expectedError)
  50. }
  51. }
  52. }
  53. func TestCommandsAtLeastTwoArguments(t *testing.T) {
  54. commands := []commandWithFunction{
  55. {"ADD", func(args []string) error { return add(nil, args, nil, "") }},
  56. {"COPY", func(args []string) error { return dispatchCopy(nil, args, nil, "") }}}
  57. for _, command := range commands {
  58. err := command.function([]string{"arg1"})
  59. if err == nil {
  60. t.Fatalf("Error should be present for %s command", command.name)
  61. }
  62. expectedError := errAtLeastTwoArguments(command.name)
  63. if err.Error() != expectedError.Error() {
  64. t.Fatalf("Wrong error message for %s. Got: %s. Should be: %s", command.name, err.Error(), expectedError)
  65. }
  66. }
  67. }
  68. func TestCommandsTooManyArguments(t *testing.T) {
  69. commands := []commandWithFunction{
  70. {"ENV", func(args []string) error { return env(nil, args, nil, "") }},
  71. {"LABEL", func(args []string) error { return label(nil, args, nil, "") }}}
  72. for _, command := range commands {
  73. err := command.function([]string{"arg1", "arg2", "arg3"})
  74. if err == nil {
  75. t.Fatalf("Error should be present for %s command", command.name)
  76. }
  77. expectedError := errTooManyArguments(command.name)
  78. if err.Error() != expectedError.Error() {
  79. t.Fatalf("Wrong error message for %s. Got: %s. Should be: %s", command.name, err.Error(), expectedError)
  80. }
  81. }
  82. }
  83. func TestCommandseBlankNames(t *testing.T) {
  84. bflags := &BFlags{}
  85. config := &container.Config{}
  86. b := &Builder{flags: bflags, runConfig: config, disableCommit: true}
  87. commands := []commandWithFunction{
  88. {"ENV", func(args []string) error { return env(b, args, nil, "") }},
  89. {"LABEL", func(args []string) error { return label(b, args, nil, "") }},
  90. }
  91. for _, command := range commands {
  92. err := command.function([]string{"", ""})
  93. if err == nil {
  94. t.Fatalf("Error should be present for %s command", command.name)
  95. }
  96. expectedError := errBlankCommandNames(command.name)
  97. if err.Error() != expectedError.Error() {
  98. t.Fatalf("Wrong error message for %s. Got: %s. Should be: %s", command.name, err.Error(), expectedError)
  99. }
  100. }
  101. }
  102. func TestEnv2Variables(t *testing.T) {
  103. variables := []string{"var1", "val1", "var2", "val2"}
  104. bflags := &BFlags{}
  105. config := &container.Config{}
  106. b := &Builder{flags: bflags, runConfig: config, disableCommit: true}
  107. if err := env(b, variables, nil, ""); err != nil {
  108. t.Fatalf("Error when executing env: %s", err.Error())
  109. }
  110. expectedVar1 := fmt.Sprintf("%s=%s", variables[0], variables[1])
  111. expectedVar2 := fmt.Sprintf("%s=%s", variables[2], variables[3])
  112. if b.runConfig.Env[0] != expectedVar1 {
  113. t.Fatalf("Wrong env output for first variable. Got: %s. Should be: %s", b.runConfig.Env[0], expectedVar1)
  114. }
  115. if b.runConfig.Env[1] != expectedVar2 {
  116. t.Fatalf("Wrong env output for second variable. Got: %s, Should be: %s", b.runConfig.Env[1], expectedVar2)
  117. }
  118. }
  119. func TestMaintainer(t *testing.T) {
  120. maintainerEntry := "Some Maintainer <maintainer@example.com>"
  121. b := &Builder{flags: &BFlags{}, runConfig: &container.Config{}, disableCommit: true}
  122. if err := maintainer(b, []string{maintainerEntry}, nil, ""); err != nil {
  123. t.Fatalf("Error when executing maintainer: %s", err.Error())
  124. }
  125. if b.maintainer != maintainerEntry {
  126. t.Fatalf("Maintainer in builder should be set to %s. Got: %s", maintainerEntry, b.maintainer)
  127. }
  128. }
  129. func TestLabel(t *testing.T) {
  130. labelName := "label"
  131. labelValue := "value"
  132. labelEntry := []string{labelName, labelValue}
  133. b := &Builder{flags: &BFlags{}, runConfig: &container.Config{}, disableCommit: true}
  134. if err := label(b, labelEntry, nil, ""); err != nil {
  135. t.Fatalf("Error when executing label: %s", err.Error())
  136. }
  137. if val, ok := b.runConfig.Labels[labelName]; ok {
  138. if val != labelValue {
  139. t.Fatalf("Label %s should have value %s, had %s instead", labelName, labelValue, val)
  140. }
  141. } else {
  142. t.Fatalf("Label %s should be present but it is not", labelName)
  143. }
  144. }
  145. func TestFrom(t *testing.T) {
  146. b := &Builder{flags: &BFlags{}, runConfig: &container.Config{}, disableCommit: true}
  147. err := from(b, []string{"scratch"}, nil, "")
  148. if runtime.GOOS == "windows" {
  149. if err == nil {
  150. t.Fatalf("Error not set on Windows")
  151. }
  152. expectedError := "Windows does not support FROM scratch"
  153. if !strings.Contains(err.Error(), expectedError) {
  154. t.Fatalf("Error message not correct on Windows. Should be: %s, got: %s", expectedError, err.Error())
  155. }
  156. } else {
  157. if err != nil {
  158. t.Fatalf("Error when executing from: %s", err.Error())
  159. }
  160. if b.image != "" {
  161. t.Fatalf("Image shoule be empty, got: %s", b.image)
  162. }
  163. if b.noBaseImage != true {
  164. t.Fatalf("Image should not have any base image, got: %v", b.noBaseImage)
  165. }
  166. }
  167. }
  168. func TestOnbuildIllegalTriggers(t *testing.T) {
  169. triggers := []struct{ command, expectedError string }{
  170. {"ONBUILD", "Chaining ONBUILD via `ONBUILD ONBUILD` isn't allowed"},
  171. {"MAINTAINER", "MAINTAINER isn't allowed as an ONBUILD trigger"},
  172. {"FROM", "FROM isn't allowed as an ONBUILD trigger"}}
  173. for _, trigger := range triggers {
  174. b := &Builder{flags: &BFlags{}, runConfig: &container.Config{}, disableCommit: true}
  175. err := onbuild(b, []string{trigger.command}, nil, "")
  176. if err == nil {
  177. t.Fatalf("Error should not be nil")
  178. }
  179. if !strings.Contains(err.Error(), trigger.expectedError) {
  180. t.Fatalf("Error message not correct. Should be: %s, got: %s", trigger.expectedError, err.Error())
  181. }
  182. }
  183. }
  184. func TestOnbuild(t *testing.T) {
  185. b := &Builder{flags: &BFlags{}, runConfig: &container.Config{}, disableCommit: true}
  186. err := onbuild(b, []string{"ADD", ".", "/app/src"}, nil, "ONBUILD ADD . /app/src")
  187. if err != nil {
  188. t.Fatalf("Error should be empty, got: %s", err.Error())
  189. }
  190. expectedOnbuild := "ADD . /app/src"
  191. if b.runConfig.OnBuild[0] != expectedOnbuild {
  192. t.Fatalf("Wrong ONBUILD command. Expected: %s, got: %s", expectedOnbuild, b.runConfig.OnBuild[0])
  193. }
  194. }
  195. func TestWorkdir(t *testing.T) {
  196. b := &Builder{flags: &BFlags{}, runConfig: &container.Config{}, disableCommit: true}
  197. workingDir := "/app"
  198. if runtime.GOOS == "windows" {
  199. workingDir = "C:\app"
  200. }
  201. err := workdir(b, []string{workingDir}, nil, "")
  202. if err != nil {
  203. t.Fatalf("Error should be empty, got: %s", err.Error())
  204. }
  205. if b.runConfig.WorkingDir != workingDir {
  206. t.Fatalf("WorkingDir should be set to %s, got %s", workingDir, b.runConfig.WorkingDir)
  207. }
  208. }
  209. func TestCmd(t *testing.T) {
  210. b := &Builder{flags: &BFlags{}, runConfig: &container.Config{}, disableCommit: true}
  211. command := "./executable"
  212. err := cmd(b, []string{command}, nil, "")
  213. if err != nil {
  214. t.Fatalf("Error should be empty, got: %s", err.Error())
  215. }
  216. var expectedCommand strslice.StrSlice
  217. if runtime.GOOS == "windows" {
  218. expectedCommand = strslice.StrSlice(append([]string{"cmd"}, "/S", "/C", command))
  219. } else {
  220. expectedCommand = strslice.StrSlice(append([]string{"/bin/sh"}, "-c", command))
  221. }
  222. if !compareStrSlice(b.runConfig.Cmd, expectedCommand) {
  223. t.Fatalf("Command should be set to %s, got %s", command, b.runConfig.Cmd)
  224. }
  225. if !b.cmdSet {
  226. t.Fatalf("Command should be marked as set")
  227. }
  228. }
  229. func compareStrSlice(slice1, slice2 strslice.StrSlice) bool {
  230. if len(slice1) != len(slice2) {
  231. return false
  232. }
  233. for i := range slice1 {
  234. if slice1[i] != slice2[i] {
  235. return false
  236. }
  237. }
  238. return true
  239. }
  240. func TestHealthcheckNone(t *testing.T) {
  241. b := &Builder{flags: &BFlags{}, runConfig: &container.Config{}, disableCommit: true}
  242. if err := healthcheck(b, []string{"NONE"}, nil, ""); err != nil {
  243. t.Fatalf("Error should be empty, got: %s", err.Error())
  244. }
  245. if b.runConfig.Healthcheck == nil {
  246. t.Fatal("Healthcheck should be set, got nil")
  247. }
  248. expectedTest := strslice.StrSlice(append([]string{"NONE"}))
  249. if !compareStrSlice(expectedTest, b.runConfig.Healthcheck.Test) {
  250. t.Fatalf("Command should be set to %s, got %s", expectedTest, b.runConfig.Healthcheck.Test)
  251. }
  252. }
  253. func TestHealthcheckCmd(t *testing.T) {
  254. b := &Builder{flags: &BFlags{flags: make(map[string]*Flag)}, runConfig: &container.Config{}, disableCommit: true}
  255. if err := healthcheck(b, []string{"CMD", "curl", "-f", "http://localhost/", "||", "exit", "1"}, nil, ""); err != nil {
  256. t.Fatalf("Error should be empty, got: %s", err.Error())
  257. }
  258. if b.runConfig.Healthcheck == nil {
  259. t.Fatal("Healthcheck should be set, got nil")
  260. }
  261. expectedTest := strslice.StrSlice(append([]string{"CMD-SHELL"}, "curl -f http://localhost/ || exit 1"))
  262. if !compareStrSlice(expectedTest, b.runConfig.Healthcheck.Test) {
  263. t.Fatalf("Command should be set to %s, got %s", expectedTest, b.runConfig.Healthcheck.Test)
  264. }
  265. }
  266. func TestEntrypoint(t *testing.T) {
  267. b := &Builder{flags: &BFlags{}, runConfig: &container.Config{}, disableCommit: true}
  268. entrypointCmd := "/usr/sbin/nginx"
  269. if err := entrypoint(b, []string{entrypointCmd}, nil, ""); err != nil {
  270. t.Fatalf("Error should be empty, got: %s", err.Error())
  271. }
  272. if b.runConfig.Entrypoint == nil {
  273. t.Fatalf("Entrypoint should be set")
  274. }
  275. var expectedEntrypoint strslice.StrSlice
  276. if runtime.GOOS == "windows" {
  277. expectedEntrypoint = strslice.StrSlice(append([]string{"cmd"}, "/S", "/C", entrypointCmd))
  278. } else {
  279. expectedEntrypoint = strslice.StrSlice(append([]string{"/bin/sh"}, "-c", entrypointCmd))
  280. }
  281. if !compareStrSlice(expectedEntrypoint, b.runConfig.Entrypoint) {
  282. t.Fatalf("Entrypoint command should be set to %s, got %s", expectedEntrypoint, b.runConfig.Entrypoint)
  283. }
  284. }
  285. func TestExpose(t *testing.T) {
  286. b := &Builder{flags: &BFlags{}, runConfig: &container.Config{}, disableCommit: true}
  287. exposedPort := "80"
  288. if err := expose(b, []string{exposedPort}, nil, ""); err != nil {
  289. t.Fatalf("Error should be empty, got: %s", err.Error())
  290. }
  291. if b.runConfig.ExposedPorts == nil {
  292. t.Fatalf("ExposedPorts should be set")
  293. }
  294. if len(b.runConfig.ExposedPorts) != 1 {
  295. t.Fatalf("ExposedPorts should contain only 1 element. Got %s", b.runConfig.ExposedPorts)
  296. }
  297. portsMapping, err := nat.ParsePortSpec(exposedPort)
  298. if err != nil {
  299. t.Fatalf("Error when parsing port spec: %s", err.Error())
  300. }
  301. if _, ok := b.runConfig.ExposedPorts[portsMapping[0].Port]; !ok {
  302. t.Fatalf("Port %s should be present. Got %s", exposedPort, b.runConfig.ExposedPorts)
  303. }
  304. }
  305. func TestUser(t *testing.T) {
  306. b := &Builder{flags: &BFlags{}, runConfig: &container.Config{}, disableCommit: true}
  307. userCommand := "foo"
  308. if err := user(b, []string{userCommand}, nil, ""); err != nil {
  309. t.Fatalf("Error should be empty, got: %s", err.Error())
  310. }
  311. if b.runConfig.User != userCommand {
  312. t.Fatalf("User should be set to %s, got %s", userCommand, b.runConfig.User)
  313. }
  314. }
  315. func TestVolume(t *testing.T) {
  316. b := &Builder{flags: &BFlags{}, runConfig: &container.Config{}, disableCommit: true}
  317. exposedVolume := "/foo"
  318. if err := volume(b, []string{exposedVolume}, nil, ""); err != nil {
  319. t.Fatalf("Error should be empty, got: %s", err.Error())
  320. }
  321. if b.runConfig.Volumes == nil {
  322. t.Fatalf("Volumes should be set")
  323. }
  324. if len(b.runConfig.Volumes) != 1 {
  325. t.Fatalf("Volumes should contain only 1 element. Got %s", b.runConfig.Volumes)
  326. }
  327. if _, ok := b.runConfig.Volumes[exposedVolume]; !ok {
  328. t.Fatalf("Volume %s should be present. Got %s", exposedVolume, b.runConfig.Volumes)
  329. }
  330. }
  331. func TestStopSignal(t *testing.T) {
  332. b := &Builder{flags: &BFlags{}, runConfig: &container.Config{}, disableCommit: true}
  333. signal := "SIGKILL"
  334. if err := stopSignal(b, []string{signal}, nil, ""); err != nil {
  335. t.Fatalf("Error should be empty, got: %s", err.Error())
  336. }
  337. if b.runConfig.StopSignal != signal {
  338. t.Fatalf("StopSignal should be set to %s, got %s", signal, b.runConfig.StopSignal)
  339. }
  340. }
  341. func TestArg(t *testing.T) {
  342. buildOptions := &types.ImageBuildOptions{BuildArgs: make(map[string]*string)}
  343. b := &Builder{flags: &BFlags{}, runConfig: &container.Config{}, disableCommit: true, allowedBuildArgs: make(map[string]bool), options: buildOptions}
  344. argName := "foo"
  345. argVal := "bar"
  346. argDef := fmt.Sprintf("%s=%s", argName, argVal)
  347. if err := arg(b, []string{argDef}, nil, ""); err != nil {
  348. t.Fatalf("Error should be empty, got: %s", err.Error())
  349. }
  350. allowed, ok := b.allowedBuildArgs[argName]
  351. if !ok {
  352. t.Fatalf("%s argument should be allowed as a build arg", argName)
  353. }
  354. if !allowed {
  355. t.Fatalf("%s argument was present in map but disallowed as a build arg", argName)
  356. }
  357. val, ok := b.options.BuildArgs[argName]
  358. if !ok {
  359. t.Fatalf("%s argument should be a build arg", argName)
  360. }
  361. if *val != "bar" {
  362. t.Fatalf("%s argument should have default value 'bar', got %s", argName, val)
  363. }
  364. }
  365. func TestShell(t *testing.T) {
  366. b := &Builder{flags: &BFlags{}, runConfig: &container.Config{}, disableCommit: true}
  367. shellCmd := "powershell"
  368. attrs := make(map[string]bool)
  369. attrs["json"] = true
  370. if err := shell(b, []string{shellCmd}, attrs, ""); err != nil {
  371. t.Fatalf("Error should be empty, got: %s", err.Error())
  372. }
  373. if b.runConfig.Shell == nil {
  374. t.Fatalf("Shell should be set")
  375. }
  376. expectedShell := strslice.StrSlice([]string{shellCmd})
  377. if !compareStrSlice(expectedShell, b.runConfig.Shell) {
  378. t.Fatalf("Shell should be set to %s, got %s", expectedShell, b.runConfig.Shell)
  379. }
  380. }