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