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