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