diff --git a/components/engine/daemon/events/events.go b/components/engine/daemon/events/events.go index d1529e1cea..6a4990fecf 100644 --- a/components/engine/daemon/events/events.go +++ b/components/engine/daemon/events/events.go @@ -28,7 +28,7 @@ func New() *Events { } } -// Subscribe adds new listener to events, returns slice of 64 stored +// Subscribe adds new listener to events, returns slice of 256 stored // last events, a channel in which you can expect new events (in form // of interface{}, so you need type assertion), and a function to call // to stop the stream of events. @@ -46,7 +46,7 @@ func (e *Events) Subscribe() ([]eventtypes.Message, chan interface{}, func()) { return current, l, cancel } -// SubscribeTopic adds new listener to events, returns slice of 64 stored +// SubscribeTopic adds new listener to events, returns slice of 256 stored // last events, a channel in which you can expect new events (in form // of interface{}, so you need type assertion). func (e *Events) SubscribeTopic(since, until time.Time, ef *Filter) ([]eventtypes.Message, chan interface{}) { diff --git a/components/engine/daemon/events/events_test.go b/components/engine/daemon/events/events_test.go index ebb222cfbd..d74f2580b9 100644 --- a/components/engine/daemon/events/events_test.go +++ b/components/engine/daemon/events/events_test.go @@ -135,21 +135,28 @@ func TestLogEvents(t *testing.T) { t.Fatalf("Must be %d events, got %d", eventsLimit, len(current)) } first := current[0] - if first.Status != "action_16" { - t.Fatalf("First action is %s, must be action_16", first.Status) + + // TODO remove this once we removed the deprecated `ID`, `Status`, and `From` fields + if first.Action != first.Status { + // Verify that the (deprecated) Status is set to the expected value + t.Fatalf("Action (%s) does not match Status (%s)", first.Action, first.Status) + } + + if first.Action != "action_16" { + t.Fatalf("First action is %s, must be action_16", first.Action) } last := current[len(current)-1] - if last.Status != "action_271" { - t.Fatalf("Last action is %s, must be action_271", last.Status) + if last.Action != "action_271" { + t.Fatalf("Last action is %s, must be action_271", last.Action) } firstC := msgs[0] - if firstC.Status != "action_272" { - t.Fatalf("First action is %s, must be action_272", firstC.Status) + if firstC.Action != "action_272" { + t.Fatalf("First action is %s, must be action_272", firstC.Action) } lastC := msgs[len(msgs)-1] - if lastC.Status != "action_281" { - t.Fatalf("Last action is %s, must be action_281", lastC.Status) + if lastC.Action != "action_281" { + t.Fatalf("Last action is %s, must be action_281", lastC.Action) } } diff --git a/components/engine/integration-cli/docker_cli_build_test.go b/components/engine/integration-cli/docker_cli_build_test.go index 89e62c14e0..e60f4d5a6e 100644 --- a/components/engine/integration-cli/docker_cli_build_test.go +++ b/components/engine/integration-cli/docker_cli_build_test.go @@ -4860,7 +4860,7 @@ func (s *DockerSuite) TestBuildBuildTimeArgDefinitionWithNoEnvInjection(c *check } } -func (s *DockerSuite) TestBuildBuildTimeArgMultipleFrom(c *check.C) { +func (s *DockerSuite) TestBuildMultiStageArg(c *check.C) { imgName := "multifrombldargtest" dockerfile := `FROM busybox ARG foo=abc @@ -4884,7 +4884,7 @@ func (s *DockerSuite) TestBuildBuildTimeArgMultipleFrom(c *check.C) { c.Assert(result.Stdout(), checker.Contains, "bar=def") } -func (s *DockerSuite) TestBuildBuildTimeFromArgMultipleFrom(c *check.C) { +func (s *DockerSuite) TestBuildMultiStageGlobalArg(c *check.C) { imgName := "multifrombldargtest" dockerfile := `ARG tag=nosuchtag FROM busybox:${tag} @@ -4909,7 +4909,7 @@ func (s *DockerSuite) TestBuildBuildTimeFromArgMultipleFrom(c *check.C) { c.Assert(result.Stdout(), checker.Contains, "tag=latest") } -func (s *DockerSuite) TestBuildBuildTimeUnusedArgMultipleFrom(c *check.C) { +func (s *DockerSuite) TestBuildMultiStageUnusedArg(c *check.C) { imgName := "multifromunusedarg" dockerfile := `FROM busybox ARG foo @@ -5727,7 +5727,7 @@ func (s *DockerSuite) TestBuildCacheFrom(c *check.C) { c.Assert(layers1[len(layers1)-1], checker.Not(checker.Equals), layers2[len(layers1)-1]) } -func (s *DockerSuite) TestBuildCacheMultipleFrom(c *check.C) { +func (s *DockerSuite) TestBuildMultiStageCache(c *check.C) { testRequires(c, DaemonIsLinux) // All tests that do save are skipped in windows dockerfile := ` FROM busybox @@ -5888,7 +5888,7 @@ func (s *DockerSuite) TestBuildContChar(c *check.C) { c.Assert(result.Combined(), checker.Contains, "Step 2/2 : RUN echo hi \\\\\n") } -func (s *DockerSuite) TestBuildCopyFromPreviousRootFS(c *check.C) { +func (s *DockerSuite) TestBuildMultiStageCopyFromSyntax(c *check.C) { dockerfile := ` FROM busybox AS first COPY foo bar @@ -5946,7 +5946,7 @@ func (s *DockerSuite) TestBuildCopyFromPreviousRootFS(c *check.C) { cli.DockerCmd(c, "run", "build4", "cat", "baz").Assert(c, icmd.Expected{Out: "pqr"}) } -func (s *DockerSuite) TestBuildCopyFromPreviousRootFSErrors(c *check.C) { +func (s *DockerSuite) TestBuildMultiStageCopyFromErrors(c *check.C) { testCases := []struct { dockerfile string expectedError string @@ -5993,7 +5993,7 @@ func (s *DockerSuite) TestBuildCopyFromPreviousRootFSErrors(c *check.C) { } } -func (s *DockerSuite) TestBuildCopyFromPreviousFrom(c *check.C) { +func (s *DockerSuite) TestBuildMultiStageMultipleBuilds(c *check.C) { dockerfile := ` FROM busybox COPY foo bar` @@ -6026,7 +6026,7 @@ func (s *DockerSuite) TestBuildCopyFromPreviousFrom(c *check.C) { c.Assert(strings.TrimSpace(out), check.Equals, "def") } -func (s *DockerSuite) TestBuildCopyFromImplicitFrom(c *check.C) { +func (s *DockerSuite) TestBuildMultiStageImplicitFrom(c *check.C) { dockerfile := ` FROM busybox COPY --from=busybox /etc/passwd /mypasswd @@ -6053,7 +6053,7 @@ func (s *DockerSuite) TestBuildCopyFromImplicitFrom(c *check.C) { } } -func (s *DockerRegistrySuite) TestBuildCopyFromImplicitPullingFrom(c *check.C) { +func (s *DockerRegistrySuite) TestBuildMultiStageImplicitPull(c *check.C) { repoName := fmt.Sprintf("%v/dockercli/testf", privateRegistryURL) dockerfile := ` @@ -6083,7 +6083,7 @@ func (s *DockerRegistrySuite) TestBuildCopyFromImplicitPullingFrom(c *check.C) { cli.Docker(cli.Args("run", "build1", "cat", "baz")).Assert(c, icmd.Expected{Out: "abc"}) } -func (s *DockerSuite) TestBuildFromPreviousBlock(c *check.C) { +func (s *DockerSuite) TestBuildMultiStageNameVariants(c *check.C) { dockerfile := ` FROM busybox as foo COPY foo / @@ -6094,7 +6094,7 @@ func (s *DockerSuite) TestBuildFromPreviousBlock(c *check.C) { FROM foo COPY --from=foo1 foo f1 COPY --from=FOo2 foo f2 - ` // foo2 case also tests that names are canse insensitive + ` // foo2 case also tests that names are case insensitive ctx := fakecontext.New(c, "", fakecontext.WithDockerfile(dockerfile), fakecontext.WithFiles(map[string]string{ @@ -6108,7 +6108,7 @@ func (s *DockerSuite) TestBuildFromPreviousBlock(c *check.C) { cli.Docker(cli.Args("run", "build1", "cat", "f2")).Assert(c, icmd.Expected{Out: "bar2"}) } -func (s *DockerTrustSuite) TestCopyFromTrustedBuild(c *check.C) { +func (s *DockerTrustSuite) TestBuildMultiStageTrusted(c *check.C) { img1 := s.setupTrustedImage(c, "trusted-build1") img2 := s.setupTrustedImage(c, "trusted-build2") dockerFile := fmt.Sprintf(` @@ -6130,7 +6130,7 @@ func (s *DockerTrustSuite) TestCopyFromTrustedBuild(c *check.C) { dockerCmdWithResult("run", name, "cat", "bar").Assert(c, icmd.Expected{Out: "ok"}) } -func (s *DockerSuite) TestBuildCopyFromPreviousFromWindows(c *check.C) { +func (s *DockerSuite) TestBuildMultiStageMultipleBuildsWindows(c *check.C) { testRequires(c, DaemonIsWindows) dockerfile := ` FROM ` + testEnv.MinimalBaseImage() + ` @@ -6218,7 +6218,7 @@ func (s *DockerSuite) TestBuildCopyFromWindowsIsCaseInsensitive(c *check.C) { } // #33176 -func (s *DockerSuite) TestBuildCopyFromResetScratch(c *check.C) { +func (s *DockerSuite) TestBuildMulitStageResetScratch(c *check.C) { testRequires(c, DaemonIsLinux) dockerfile := ` diff --git a/components/engine/integration-cli/docker_cli_events_test.go b/components/engine/integration-cli/docker_cli_events_test.go index dff54a4463..b75dcc1512 100644 --- a/components/engine/integration-cli/docker_cli_events_test.go +++ b/components/engine/integration-cli/docker_cli_events_test.go @@ -81,50 +81,6 @@ func (s *DockerSuite) TestEventsUntag(c *check.C) { } } -func (s *DockerSuite) TestEventsLimit(c *check.C) { - // Windows: Limit to 4 goroutines creating containers in order to prevent - // timeouts creating so many containers simultaneously. This is a due to - // a bug in the Windows platform. It will be fixed in a Windows Update. - numContainers := 17 - eventPerContainer := 7 // create, attach, network connect, start, die, network disconnect, destroy - numConcurrentContainers := numContainers - if testEnv.DaemonPlatform() == "windows" { - numConcurrentContainers = 4 - } - sem := make(chan bool, numConcurrentContainers) - errChan := make(chan error, numContainers) - - startTime := daemonUnixTime(c) - - args := []string{"run", "--rm", "busybox", "true"} - for i := 0; i < numContainers; i++ { - sem <- true - go func(i int) { - defer func() { <-sem }() - out, err := exec.Command(dockerBinary, args...).CombinedOutput() - if err != nil { - err = fmt.Errorf("%v: %s", err, string(out)) - } - errChan <- err - }(i) - } - - // Wait for all goroutines to finish - for i := 0; i < cap(sem); i++ { - sem <- true - } - close(errChan) - - for err := range errChan { - c.Assert(err, checker.IsNil, check.Commentf("%q failed with error", strings.Join(args, " "))) - } - - out, _ := dockerCmd(c, "events", "--since="+startTime, "--until", daemonUnixTime(c)) - events := strings.Split(out, "\n") - nEvents := len(events) - 1 - c.Assert(nEvents, checker.Equals, numContainers*eventPerContainer, check.Commentf("events should be limited to 256, but received %d", nEvents)) -} - func (s *DockerSuite) TestEventsContainerEvents(c *check.C) { dockerCmd(c, "run", "--rm", "--name", "container-events-test", "busybox", "true") diff --git a/components/engine/integration/build/build_test.go b/components/engine/integration/build/build_test.go index b447b623cc..1271daea9b 100644 --- a/components/engine/integration/build/build_test.go +++ b/components/engine/integration/build/build_test.go @@ -197,3 +197,73 @@ func TestBuildWithEmptyLayers(t *testing.T) { resp.Body.Close() require.NoError(t, err) } + +// TestBuildMultiStageOnBuild checks that ONBUILD commands are applied to +// multiple subsequent stages +// #35652 +func TestBuildMultiStageOnBuild(t *testing.T) { + defer setupTest(t)() + // test both metadata and layer based commands as they may be implemented differently + dockerfile := `FROM busybox AS stage1 +ONBUILD RUN echo 'foo' >somefile +ONBUILD ENV bar=baz + +FROM stage1 +RUN cat somefile # fails if ONBUILD RUN fails + +FROM stage1 +RUN cat somefile` + + ctx := context.Background() + source := fakecontext.New(t, "", + fakecontext.WithDockerfile(dockerfile)) + defer source.Close() + + apiclient := testEnv.APIClient() + resp, err := apiclient.ImageBuild(ctx, + source.AsTarReader(t), + types.ImageBuildOptions{ + Remove: true, + ForceRemove: true, + }) + + out := bytes.NewBuffer(nil) + require.NoError(t, err) + _, err = io.Copy(out, resp.Body) + resp.Body.Close() + require.NoError(t, err) + + assert.Contains(t, out.String(), "Successfully built") + + imageIDs, err := getImageIDsFromBuild(out.Bytes()) + require.NoError(t, err) + assert.Equal(t, 3, len(imageIDs)) + + image, _, err := apiclient.ImageInspectWithRaw(context.Background(), imageIDs[2]) + require.NoError(t, err) + assert.Contains(t, image.Config.Env, "bar=baz") +} + +type buildLine struct { + Stream string + Aux struct { + ID string + } +} + +func getImageIDsFromBuild(output []byte) ([]string, error) { + ids := []string{} + for _, line := range bytes.Split(output, []byte("\n")) { + if len(line) == 0 { + continue + } + entry := buildLine{} + if err := json.Unmarshal(line, &entry); err != nil { + return nil, err + } + if entry.Aux.ID != "" { + ids = append(ids, entry.Aux.ID) + } + } + return ids, nil +}