github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/engine/builder/dockerfile/dispatchers_test.go (about) 1 package dockerfile // import "github.com/docker/docker/builder/dockerfile" 2 3 import ( 4 "bytes" 5 "context" 6 "fmt" 7 "runtime" 8 "strings" 9 "testing" 10 11 "github.com/docker/docker/api/types" 12 "github.com/docker/docker/api/types/backend" 13 "github.com/docker/docker/api/types/container" 14 "github.com/docker/docker/api/types/strslice" 15 "github.com/docker/docker/builder" 16 "github.com/docker/docker/image" 17 "github.com/docker/docker/pkg/system" 18 "github.com/docker/go-connections/nat" 19 "github.com/moby/buildkit/frontend/dockerfile/instructions" 20 "github.com/moby/buildkit/frontend/dockerfile/parser" 21 "github.com/moby/buildkit/frontend/dockerfile/shell" 22 "gotest.tools/v3/assert" 23 is "gotest.tools/v3/assert/cmp" 24 ) 25 26 func newBuilderWithMockBackend() *Builder { 27 mockBackend := &MockBackend{} 28 opts := &types.ImageBuildOptions{} 29 ctx := context.Background() 30 b := &Builder{ 31 options: opts, 32 docker: mockBackend, 33 Stdout: new(bytes.Buffer), 34 clientCtx: ctx, 35 disableCommit: true, 36 imageSources: newImageSources(ctx, builderOptions{ 37 Options: opts, 38 Backend: mockBackend, 39 }), 40 imageProber: newImageProber(mockBackend, nil, false), 41 containerManager: newContainerManager(mockBackend), 42 } 43 return b 44 } 45 46 func TestEnv2Variables(t *testing.T) { 47 b := newBuilderWithMockBackend() 48 sb := newDispatchRequest(b, '\\', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults()) 49 envCommand := &instructions.EnvCommand{ 50 Env: instructions.KeyValuePairs{ 51 instructions.KeyValuePair{Key: "var1", Value: "val1"}, 52 instructions.KeyValuePair{Key: "var2", Value: "val2"}, 53 }, 54 } 55 err := dispatch(sb, envCommand) 56 assert.NilError(t, err) 57 58 expected := []string{ 59 "var1=val1", 60 "var2=val2", 61 } 62 assert.Check(t, is.DeepEqual(expected, sb.state.runConfig.Env)) 63 } 64 65 func TestEnvValueWithExistingRunConfigEnv(t *testing.T) { 66 b := newBuilderWithMockBackend() 67 sb := newDispatchRequest(b, '\\', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults()) 68 sb.state.runConfig.Env = []string{"var1=old", "var2=fromenv"} 69 envCommand := &instructions.EnvCommand{ 70 Env: instructions.KeyValuePairs{ 71 instructions.KeyValuePair{Key: "var1", Value: "val1"}, 72 }, 73 } 74 err := dispatch(sb, envCommand) 75 assert.NilError(t, err) 76 expected := []string{ 77 "var1=val1", 78 "var2=fromenv", 79 } 80 assert.Check(t, is.DeepEqual(expected, sb.state.runConfig.Env)) 81 } 82 83 func TestMaintainer(t *testing.T) { 84 maintainerEntry := "Some Maintainer <maintainer@example.com>" 85 b := newBuilderWithMockBackend() 86 sb := newDispatchRequest(b, '\\', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults()) 87 cmd := &instructions.MaintainerCommand{Maintainer: maintainerEntry} 88 err := dispatch(sb, cmd) 89 assert.NilError(t, err) 90 assert.Check(t, is.Equal(maintainerEntry, sb.state.maintainer)) 91 } 92 93 func TestLabel(t *testing.T) { 94 labelName := "label" 95 labelValue := "value" 96 97 b := newBuilderWithMockBackend() 98 sb := newDispatchRequest(b, '\\', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults()) 99 cmd := &instructions.LabelCommand{ 100 Labels: instructions.KeyValuePairs{ 101 instructions.KeyValuePair{Key: labelName, Value: labelValue}, 102 }, 103 } 104 err := dispatch(sb, cmd) 105 assert.NilError(t, err) 106 107 assert.Assert(t, is.Contains(sb.state.runConfig.Labels, labelName)) 108 assert.Check(t, is.Equal(sb.state.runConfig.Labels[labelName], labelValue)) 109 } 110 111 func TestFromScratch(t *testing.T) { 112 b := newBuilderWithMockBackend() 113 sb := newDispatchRequest(b, '\\', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults()) 114 cmd := &instructions.Stage{ 115 BaseName: "scratch", 116 } 117 err := initializeStage(sb, cmd) 118 119 if runtime.GOOS == "windows" && !system.LCOWSupported() { 120 assert.Check(t, is.Error(err, "Linux containers are not supported on this system")) 121 return 122 } 123 124 assert.NilError(t, err) 125 assert.Check(t, sb.state.hasFromImage()) 126 assert.Check(t, is.Equal("", sb.state.imageID)) 127 expected := "PATH=" + system.DefaultPathEnv(runtime.GOOS) 128 assert.Check(t, is.DeepEqual([]string{expected}, sb.state.runConfig.Env)) 129 } 130 131 func TestFromWithArg(t *testing.T) { 132 tag, expected := ":sometag", "expectedthisid" 133 134 getImage := func(name string) (builder.Image, builder.ROLayer, error) { 135 assert.Check(t, is.Equal("alpine"+tag, name)) 136 return &mockImage{id: "expectedthisid"}, nil, nil 137 } 138 b := newBuilderWithMockBackend() 139 b.docker.(*MockBackend).getImageFunc = getImage 140 args := NewBuildArgs(make(map[string]*string)) 141 142 val := "sometag" 143 metaArg := instructions.ArgCommand{Args: []instructions.KeyValuePairOptional{{ 144 Key: "THETAG", 145 Value: &val, 146 }}} 147 cmd := &instructions.Stage{ 148 BaseName: "alpine:${THETAG}", 149 } 150 err := processMetaArg(metaArg, shell.NewLex('\\'), args) 151 152 sb := newDispatchRequest(b, '\\', nil, args, newStagesBuildResults()) 153 assert.NilError(t, err) 154 err = initializeStage(sb, cmd) 155 assert.NilError(t, err) 156 157 assert.Check(t, is.Equal(expected, sb.state.imageID)) 158 assert.Check(t, is.Equal(expected, sb.state.baseImage.ImageID())) 159 assert.Check(t, is.Len(sb.state.buildArgs.GetAllAllowed(), 0)) 160 assert.Check(t, is.Len(sb.state.buildArgs.GetAllMeta(), 1)) 161 } 162 163 func TestFromWithArgButBuildArgsNotGiven(t *testing.T) { 164 b := newBuilderWithMockBackend() 165 args := NewBuildArgs(make(map[string]*string)) 166 167 metaArg := instructions.ArgCommand{} 168 cmd := &instructions.Stage{ 169 BaseName: "${THETAG}", 170 } 171 err := processMetaArg(metaArg, shell.NewLex('\\'), args) 172 173 sb := newDispatchRequest(b, '\\', nil, args, newStagesBuildResults()) 174 assert.NilError(t, err) 175 err = initializeStage(sb, cmd) 176 assert.Error(t, err, "base name (${THETAG}) should not be blank") 177 } 178 179 func TestFromWithUndefinedArg(t *testing.T) { 180 tag, expected := "sometag", "expectedthisid" 181 182 getImage := func(name string) (builder.Image, builder.ROLayer, error) { 183 assert.Check(t, is.Equal("alpine", name)) 184 return &mockImage{id: "expectedthisid"}, nil, nil 185 } 186 b := newBuilderWithMockBackend() 187 b.docker.(*MockBackend).getImageFunc = getImage 188 sb := newDispatchRequest(b, '\\', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults()) 189 190 b.options.BuildArgs = map[string]*string{"THETAG": &tag} 191 192 cmd := &instructions.Stage{ 193 BaseName: "alpine${THETAG}", 194 } 195 err := initializeStage(sb, cmd) 196 assert.NilError(t, err) 197 assert.Check(t, is.Equal(expected, sb.state.imageID)) 198 } 199 200 func TestFromMultiStageWithNamedStage(t *testing.T) { 201 b := newBuilderWithMockBackend() 202 firstFrom := &instructions.Stage{BaseName: "someimg", Name: "base"} 203 secondFrom := &instructions.Stage{BaseName: "base"} 204 previousResults := newStagesBuildResults() 205 firstSB := newDispatchRequest(b, '\\', nil, NewBuildArgs(make(map[string]*string)), previousResults) 206 secondSB := newDispatchRequest(b, '\\', nil, NewBuildArgs(make(map[string]*string)), previousResults) 207 err := initializeStage(firstSB, firstFrom) 208 assert.NilError(t, err) 209 assert.Check(t, firstSB.state.hasFromImage()) 210 previousResults.indexed["base"] = firstSB.state.runConfig 211 previousResults.flat = append(previousResults.flat, firstSB.state.runConfig) 212 err = initializeStage(secondSB, secondFrom) 213 assert.NilError(t, err) 214 assert.Check(t, secondSB.state.hasFromImage()) 215 } 216 217 func TestOnbuild(t *testing.T) { 218 b := newBuilderWithMockBackend() 219 sb := newDispatchRequest(b, '\\', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults()) 220 cmd := &instructions.OnbuildCommand{ 221 Expression: "ADD . /app/src", 222 } 223 err := dispatch(sb, cmd) 224 assert.NilError(t, err) 225 assert.Check(t, is.Equal("ADD . /app/src", sb.state.runConfig.OnBuild[0])) 226 } 227 228 func TestWorkdir(t *testing.T) { 229 b := newBuilderWithMockBackend() 230 sb := newDispatchRequest(b, '`', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults()) 231 sb.state.baseImage = &mockImage{} 232 workingDir := "/app" 233 if runtime.GOOS == "windows" { 234 workingDir = "C:\\app" 235 } 236 cmd := &instructions.WorkdirCommand{ 237 Path: workingDir, 238 } 239 240 err := dispatch(sb, cmd) 241 assert.NilError(t, err) 242 assert.Check(t, is.Equal(workingDir, sb.state.runConfig.WorkingDir)) 243 } 244 245 func TestCmd(t *testing.T) { 246 b := newBuilderWithMockBackend() 247 sb := newDispatchRequest(b, '`', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults()) 248 sb.state.baseImage = &mockImage{} 249 command := "./executable" 250 251 cmd := &instructions.CmdCommand{ 252 ShellDependantCmdLine: instructions.ShellDependantCmdLine{ 253 CmdLine: strslice.StrSlice{command}, 254 PrependShell: true, 255 }, 256 } 257 err := dispatch(sb, cmd) 258 assert.NilError(t, err) 259 260 var expectedCommand strslice.StrSlice 261 if runtime.GOOS == "windows" { 262 expectedCommand = strslice.StrSlice(append([]string{"cmd"}, "/S", "/C", command)) 263 } else { 264 expectedCommand = strslice.StrSlice(append([]string{"/bin/sh"}, "-c", command)) 265 } 266 267 assert.Check(t, is.DeepEqual(expectedCommand, sb.state.runConfig.Cmd)) 268 assert.Check(t, sb.state.cmdSet) 269 } 270 271 func TestHealthcheckNone(t *testing.T) { 272 b := newBuilderWithMockBackend() 273 sb := newDispatchRequest(b, '`', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults()) 274 cmd := &instructions.HealthCheckCommand{ 275 Health: &container.HealthConfig{ 276 Test: []string{"NONE"}, 277 }, 278 } 279 err := dispatch(sb, cmd) 280 assert.NilError(t, err) 281 282 assert.Assert(t, sb.state.runConfig.Healthcheck != nil) 283 assert.Check(t, is.DeepEqual([]string{"NONE"}, sb.state.runConfig.Healthcheck.Test)) 284 } 285 286 func TestHealthcheckCmd(t *testing.T) { 287 288 b := newBuilderWithMockBackend() 289 sb := newDispatchRequest(b, '`', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults()) 290 expectedTest := []string{"CMD-SHELL", "curl -f http://localhost/ || exit 1"} 291 cmd := &instructions.HealthCheckCommand{ 292 Health: &container.HealthConfig{ 293 Test: expectedTest, 294 }, 295 } 296 err := dispatch(sb, cmd) 297 assert.NilError(t, err) 298 299 assert.Assert(t, sb.state.runConfig.Healthcheck != nil) 300 assert.Check(t, is.DeepEqual(expectedTest, sb.state.runConfig.Healthcheck.Test)) 301 } 302 303 func TestEntrypoint(t *testing.T) { 304 b := newBuilderWithMockBackend() 305 sb := newDispatchRequest(b, '`', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults()) 306 sb.state.baseImage = &mockImage{} 307 entrypointCmd := "/usr/sbin/nginx" 308 309 cmd := &instructions.EntrypointCommand{ 310 ShellDependantCmdLine: instructions.ShellDependantCmdLine{ 311 CmdLine: strslice.StrSlice{entrypointCmd}, 312 PrependShell: true, 313 }, 314 } 315 err := dispatch(sb, cmd) 316 assert.NilError(t, err) 317 assert.Assert(t, sb.state.runConfig.Entrypoint != nil) 318 319 var expectedEntrypoint strslice.StrSlice 320 if runtime.GOOS == "windows" { 321 expectedEntrypoint = strslice.StrSlice(append([]string{"cmd"}, "/S", "/C", entrypointCmd)) 322 } else { 323 expectedEntrypoint = strslice.StrSlice(append([]string{"/bin/sh"}, "-c", entrypointCmd)) 324 } 325 assert.Check(t, is.DeepEqual(expectedEntrypoint, sb.state.runConfig.Entrypoint)) 326 } 327 328 func TestExpose(t *testing.T) { 329 b := newBuilderWithMockBackend() 330 sb := newDispatchRequest(b, '`', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults()) 331 332 exposedPort := "80" 333 cmd := &instructions.ExposeCommand{ 334 Ports: []string{exposedPort}, 335 } 336 err := dispatch(sb, cmd) 337 assert.NilError(t, err) 338 339 assert.Assert(t, sb.state.runConfig.ExposedPorts != nil) 340 assert.Assert(t, is.Len(sb.state.runConfig.ExposedPorts, 1)) 341 342 portsMapping, err := nat.ParsePortSpec(exposedPort) 343 assert.NilError(t, err) 344 assert.Check(t, is.Contains(sb.state.runConfig.ExposedPorts, portsMapping[0].Port)) 345 } 346 347 func TestUser(t *testing.T) { 348 b := newBuilderWithMockBackend() 349 sb := newDispatchRequest(b, '`', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults()) 350 351 cmd := &instructions.UserCommand{ 352 User: "test", 353 } 354 err := dispatch(sb, cmd) 355 assert.NilError(t, err) 356 assert.Check(t, is.Equal("test", sb.state.runConfig.User)) 357 } 358 359 func TestVolume(t *testing.T) { 360 b := newBuilderWithMockBackend() 361 sb := newDispatchRequest(b, '`', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults()) 362 363 exposedVolume := "/foo" 364 365 cmd := &instructions.VolumeCommand{ 366 Volumes: []string{exposedVolume}, 367 } 368 err := dispatch(sb, cmd) 369 assert.NilError(t, err) 370 assert.Assert(t, sb.state.runConfig.Volumes != nil) 371 assert.Check(t, is.Len(sb.state.runConfig.Volumes, 1)) 372 assert.Check(t, is.Contains(sb.state.runConfig.Volumes, exposedVolume)) 373 } 374 375 func TestStopSignal(t *testing.T) { 376 if runtime.GOOS == "windows" { 377 t.Skip("Windows does not support stopsignal") 378 return 379 } 380 b := newBuilderWithMockBackend() 381 sb := newDispatchRequest(b, '`', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults()) 382 sb.state.baseImage = &mockImage{} 383 signal := "SIGKILL" 384 385 cmd := &instructions.StopSignalCommand{ 386 Signal: signal, 387 } 388 err := dispatch(sb, cmd) 389 assert.NilError(t, err) 390 assert.Check(t, is.Equal(signal, sb.state.runConfig.StopSignal)) 391 } 392 393 func TestArg(t *testing.T) { 394 b := newBuilderWithMockBackend() 395 sb := newDispatchRequest(b, '`', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults()) 396 397 argName := "foo" 398 argVal := "bar" 399 cmd := &instructions.ArgCommand{Args: []instructions.KeyValuePairOptional{{Key: argName, Value: &argVal}}} 400 err := dispatch(sb, cmd) 401 assert.NilError(t, err) 402 403 expected := map[string]string{argName: argVal} 404 assert.Check(t, is.DeepEqual(expected, sb.state.buildArgs.GetAllAllowed())) 405 } 406 407 func TestShell(t *testing.T) { 408 b := newBuilderWithMockBackend() 409 sb := newDispatchRequest(b, '`', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults()) 410 411 shellCmd := "powershell" 412 cmd := &instructions.ShellCommand{Shell: strslice.StrSlice{shellCmd}} 413 414 err := dispatch(sb, cmd) 415 assert.NilError(t, err) 416 417 expectedShell := strslice.StrSlice([]string{shellCmd}) 418 assert.Check(t, is.DeepEqual(expectedShell, sb.state.runConfig.Shell)) 419 } 420 421 func TestPrependEnvOnCmd(t *testing.T) { 422 buildArgs := NewBuildArgs(nil) 423 buildArgs.AddArg("NO_PROXY", nil) 424 425 args := []string{"sorted=nope", "args=not", "http_proxy=foo", "NO_PROXY=YA"} 426 cmd := []string{"foo", "bar"} 427 cmdWithEnv := prependEnvOnCmd(buildArgs, args, cmd) 428 expected := strslice.StrSlice([]string{ 429 "|3", "NO_PROXY=YA", "args=not", "sorted=nope", "foo", "bar"}) 430 assert.Check(t, is.DeepEqual(expected, cmdWithEnv)) 431 } 432 433 func TestRunWithBuildArgs(t *testing.T) { 434 b := newBuilderWithMockBackend() 435 args := NewBuildArgs(make(map[string]*string)) 436 args.argsFromOptions["HTTP_PROXY"] = strPtr("FOO") 437 b.disableCommit = false 438 sb := newDispatchRequest(b, '`', nil, args, newStagesBuildResults()) 439 440 runConfig := &container.Config{} 441 origCmd := strslice.StrSlice([]string{"cmd", "in", "from", "image"}) 442 443 var cmdWithShell strslice.StrSlice 444 if runtime.GOOS == "windows" { 445 cmdWithShell = strslice.StrSlice([]string{strings.Join(append(getShell(runConfig, runtime.GOOS), []string{"echo foo"}...), " ")}) 446 } else { 447 cmdWithShell = strslice.StrSlice(append(getShell(runConfig, runtime.GOOS), "echo foo")) 448 } 449 450 envVars := []string{"|1", "one=two"} 451 cachedCmd := strslice.StrSlice(append(envVars, cmdWithShell...)) 452 453 imageCache := &mockImageCache{ 454 getCacheFunc: func(parentID string, cfg *container.Config) (string, error) { 455 // Check the runConfig.Cmd sent to probeCache() 456 assert.Check(t, is.DeepEqual(cachedCmd, cfg.Cmd)) 457 assert.Check(t, is.DeepEqual(strslice.StrSlice(nil), cfg.Entrypoint)) 458 return "", nil 459 }, 460 } 461 462 mockBackend := b.docker.(*MockBackend) 463 mockBackend.makeImageCacheFunc = func(_ []string) builder.ImageCache { 464 return imageCache 465 } 466 b.imageProber = newImageProber(mockBackend, nil, false) 467 mockBackend.getImageFunc = func(_ string) (builder.Image, builder.ROLayer, error) { 468 return &mockImage{ 469 id: "abcdef", 470 config: &container.Config{Cmd: origCmd}, 471 }, nil, nil 472 } 473 mockBackend.containerCreateFunc = func(config types.ContainerCreateConfig) (container.ContainerCreateCreatedBody, error) { 474 // Check the runConfig.Cmd sent to create() 475 assert.Check(t, is.DeepEqual(cmdWithShell, config.Config.Cmd)) 476 assert.Check(t, is.Contains(config.Config.Env, "one=two")) 477 assert.Check(t, is.DeepEqual(strslice.StrSlice{""}, config.Config.Entrypoint)) 478 return container.ContainerCreateCreatedBody{ID: "12345"}, nil 479 } 480 mockBackend.commitFunc = func(cfg backend.CommitConfig) (image.ID, error) { 481 // Check the runConfig.Cmd sent to commit() 482 assert.Check(t, is.DeepEqual(origCmd, cfg.Config.Cmd)) 483 assert.Check(t, is.DeepEqual(cachedCmd, cfg.ContainerConfig.Cmd)) 484 assert.Check(t, is.DeepEqual(strslice.StrSlice(nil), cfg.Config.Entrypoint)) 485 return "", nil 486 } 487 from := &instructions.Stage{BaseName: "abcdef"} 488 err := initializeStage(sb, from) 489 assert.NilError(t, err) 490 sb.state.buildArgs.AddArg("one", strPtr("two")) 491 492 // This is hugely annoying. On the Windows side, it relies on the 493 // RunCommand being able to emit String() and Name() (as implemented by 494 // withNameAndCode). Unfortunately, that is internal, and no way to directly 495 // set. However, we can fortunately use ParseInstruction in the instructions 496 // package to parse a fake node which can be used as our instructions.RunCommand 497 // instead. 498 node := &parser.Node{ 499 Original: `RUN echo foo`, 500 Value: "run", 501 } 502 runint, err := instructions.ParseInstruction(node) 503 assert.NilError(t, err) 504 runinst := runint.(*instructions.RunCommand) 505 runinst.CmdLine = strslice.StrSlice{"echo foo"} 506 runinst.PrependShell = true 507 508 assert.NilError(t, dispatch(sb, runinst)) 509 510 // Check that runConfig.Cmd has not been modified by run 511 assert.Check(t, is.DeepEqual(origCmd, sb.state.runConfig.Cmd)) 512 } 513 514 func TestRunIgnoresHealthcheck(t *testing.T) { 515 b := newBuilderWithMockBackend() 516 args := NewBuildArgs(make(map[string]*string)) 517 sb := newDispatchRequest(b, '`', nil, args, newStagesBuildResults()) 518 b.disableCommit = false 519 520 origCmd := strslice.StrSlice([]string{"cmd", "in", "from", "image"}) 521 522 imageCache := &mockImageCache{ 523 getCacheFunc: func(parentID string, cfg *container.Config) (string, error) { 524 return "", nil 525 }, 526 } 527 528 mockBackend := b.docker.(*MockBackend) 529 mockBackend.makeImageCacheFunc = func(_ []string) builder.ImageCache { 530 return imageCache 531 } 532 b.imageProber = newImageProber(mockBackend, nil, false) 533 mockBackend.getImageFunc = func(_ string) (builder.Image, builder.ROLayer, error) { 534 return &mockImage{ 535 id: "abcdef", 536 config: &container.Config{Cmd: origCmd}, 537 }, nil, nil 538 } 539 mockBackend.containerCreateFunc = func(config types.ContainerCreateConfig) (container.ContainerCreateCreatedBody, error) { 540 return container.ContainerCreateCreatedBody{ID: "12345"}, nil 541 } 542 mockBackend.commitFunc = func(cfg backend.CommitConfig) (image.ID, error) { 543 return "", nil 544 } 545 from := &instructions.Stage{BaseName: "abcdef"} 546 err := initializeStage(sb, from) 547 assert.NilError(t, err) 548 549 expectedTest := []string{"CMD-SHELL", "curl -f http://localhost/ || exit 1"} 550 cmd := &instructions.HealthCheckCommand{ 551 Health: &container.HealthConfig{ 552 Test: expectedTest, 553 }, 554 } 555 assert.NilError(t, dispatch(sb, cmd)) 556 assert.Assert(t, sb.state.runConfig.Healthcheck != nil) 557 558 mockBackend.containerCreateFunc = func(config types.ContainerCreateConfig) (container.ContainerCreateCreatedBody, error) { 559 // Check the Healthcheck is disabled. 560 assert.Check(t, is.DeepEqual([]string{"NONE"}, config.Config.Healthcheck.Test)) 561 return container.ContainerCreateCreatedBody{ID: "123456"}, nil 562 } 563 564 sb.state.buildArgs.AddArg("one", strPtr("two")) 565 run := &instructions.RunCommand{ 566 ShellDependantCmdLine: instructions.ShellDependantCmdLine{ 567 CmdLine: strslice.StrSlice{"echo foo"}, 568 PrependShell: true, 569 }, 570 } 571 assert.NilError(t, dispatch(sb, run)) 572 assert.Check(t, is.DeepEqual(expectedTest, sb.state.runConfig.Healthcheck.Test)) 573 } 574 575 func TestDispatchUnsupportedOptions(t *testing.T) { 576 b := newBuilderWithMockBackend() 577 sb := newDispatchRequest(b, '`', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults()) 578 sb.state.baseImage = &mockImage{} 579 sb.state.operatingSystem = runtime.GOOS 580 581 t.Run("ADD with chmod", func(t *testing.T) { 582 cmd := &instructions.AddCommand{SourcesAndDest: []string{".", "."}, Chmod: "0655"} 583 err := dispatch(sb, cmd) 584 assert.Error(t, err, "the --chmod option requires BuildKit. Refer to https://docs.docker.com/go/buildkit/ to learn how to build images with BuildKit enabled") 585 }) 586 587 t.Run("COPY with chmod", func(t *testing.T) { 588 cmd := &instructions.CopyCommand{SourcesAndDest: []string{".", "."}, Chmod: "0655"} 589 err := dispatch(sb, cmd) 590 assert.Error(t, err, "the --chmod option requires BuildKit. Refer to https://docs.docker.com/go/buildkit/ to learn how to build images with BuildKit enabled") 591 }) 592 593 t.Run("RUN with unsupported options", func(t *testing.T) { 594 cmd := &instructions.RunCommand{ 595 ShellDependantCmdLine: instructions.ShellDependantCmdLine{ 596 CmdLine: strslice.StrSlice{"echo foo"}, 597 PrependShell: true, 598 }, 599 } 600 601 // classic builder "RUN" currently doesn't support any flags, but testing 602 // both "known" flags and "bogus" flags for completeness, and in case 603 // one or more of these flags will be supported in future 604 for _, f := range []string{"mount", "network", "security", "any-flag"} { 605 cmd.FlagsUsed = []string{f} 606 err := dispatch(sb, cmd) 607 assert.Error(t, err, fmt.Sprintf("the --%s option requires BuildKit. Refer to https://docs.docker.com/go/buildkit/ to learn how to build images with BuildKit enabled", f)) 608 } 609 }) 610 }