1
0
mirror of https://github.com/moby/moby.git synced 2025-04-18 20:44:11 +03:00
moby/integration-cli/docker_cli_events_unix_test.go
Sebastiaan van Stijn 1932091e21
remove support for non-distributable artifacts and deprecate API fields and config
Non-distributable artifacts (foreign layers) were introduced in commit
05bd04350b8348b3c3bbe3156420257313e4e804 to accommodate Windows images,
for which the EULA did not allow layers to be distributed through registries
other than those hosted by Microsoft. The concept of foreign / non-distributable
layers was adopted by the OCI distribution spec in [oci#233].

These restrictions were relaxed later to allow distributing these images
through non-public registries, for which a configuration was added in the
daemon in 67fdf574d5acd6ddccb6ece0ffe0ace1c1608712. In 2022, Microsoft updated
the EULA and [removed these restrictions altogether][1], and the OCI distribution
spec deprecated the  functionality in [oci#965].

In 2023, Microsoft [removed the use of foreign data layers][2] for their images,
making this functionality obsolete.

This patch:

- Deprecates the `--allow-nondistributable-artifacts` daemon flag and corresponding
  `allow-nondistributable-artifacts` field in `daemon.json`. Setting either
  option will no longer take an effect, but a deprecation warning log is added
  to raise awareness about the deprecation. This warning is planned to become
  an error in the next release.
- Deprecates the `RegistryConfig.AllowNondistributableArtifactsCIDRs` and
  `RegistryConfig.AllowNondistributableArtifactsHostnames` fields in the
  `GET /info` API response. For API version v1.48 and lower, the fields are
  still included in the response, but always `null`. In API version v1.49 and
  higher, the field will be omitted entirely.
- Deprecates the `api/types/registry/ServiceConfig.AllowNondistributableArtifactsCIDRs`
  field.
- Deprecates the `api/types/registry/ServiceConfig.AllowNondistributableArtifactsHostnames`
  field.
- Deprecates the `registry.ServiceOptions.AllowNondistributableArtifacts` field.

[oci#233]: https://github.com/opencontainers/image-spec/pull/233
[oci#965]: https://github.com/opencontainers/image-spec/pull/965
[1]: https://techcommunity.microsoft.com/blog/containers/announcing-windows-container-base-image-redistribution-rights-change/3645201
[2]: https://techcommunity.microsoft.com/blog/containers/announcing-removal-of-foreign-layers-from-windows-container-images/3846833

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-12-11 20:29:46 +01:00

472 lines
16 KiB
Go

//go:build !windows
package main
import (
"bufio"
"bytes"
"fmt"
"os"
"os/exec"
"strings"
"testing"
"time"
"unicode"
"github.com/creack/pty"
"github.com/docker/docker/integration-cli/cli"
"github.com/docker/docker/integration-cli/cli/build"
"golang.org/x/sys/unix"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
"gotest.tools/v3/skip"
)
// #5979
func (s *DockerCLIEventSuite) TestEventsRedirectStdout(c *testing.T) {
since := daemonUnixTime(c)
cli.DockerCmd(c, "run", "busybox", "true")
file, err := os.CreateTemp("", "")
assert.NilError(c, err, "could not create temp file")
defer os.Remove(file.Name())
command := fmt.Sprintf("%s events --since=%s --until=%s > %s", dockerBinary, since, daemonUnixTime(c), file.Name())
_, tty, err := pty.Open()
assert.NilError(c, err, "Could not open pty")
cmd := exec.Command("sh", "-c", command)
cmd.Stdin = tty
cmd.Stdout = tty
cmd.Stderr = tty
assert.NilError(c, cmd.Run(), "run err for command %q", command)
scanner := bufio.NewScanner(file)
for scanner.Scan() {
for _, ch := range scanner.Text() {
assert.Check(c, unicode.IsControl(ch) == false, "found control character %v", []byte(string(ch)))
}
}
assert.NilError(c, scanner.Err(), "Scan err for command %q", command)
}
func (s *DockerCLIEventSuite) TestEventsOOMDisableFalse(c *testing.T) {
testRequires(c, DaemonIsLinux, oomControl, memoryLimitSupport, swapMemorySupport, NotPpc64le)
skip.If(c, GitHubActions, "FIXME: https://github.com/moby/moby/pull/36541")
errChan := make(chan error, 1)
go func() {
defer close(errChan)
out, exitCode, _ := dockerCmdWithError("run", "--name", "oomFalse", "-m", "10MB", "busybox", "sh", "-c", "x=a; while true; do x=$x$x$x$x; done")
if expected := 137; exitCode != expected {
errChan <- fmt.Errorf("wrong exit code for OOM container: expected %d, got %d (output: %q)", expected, exitCode, out)
}
}()
select {
case err := <-errChan:
assert.NilError(c, err)
case <-time.After(30 * time.Second):
c.Fatal("Timeout waiting for container to die on OOM")
}
out := cli.DockerCmd(c, "events", "--since=0", "-f", "container=oomFalse", "--until", daemonUnixTime(c)).Stdout()
events := strings.Split(strings.TrimSuffix(out, "\n"), "\n")
nEvents := len(events)
assert.Assert(c, nEvents >= 5)
assert.Equal(c, parseEventAction(c, events[nEvents-5]), "create")
assert.Equal(c, parseEventAction(c, events[nEvents-4]), "attach")
assert.Equal(c, parseEventAction(c, events[nEvents-3]), "start")
assert.Equal(c, parseEventAction(c, events[nEvents-2]), "oom")
assert.Equal(c, parseEventAction(c, events[nEvents-1]), "die")
}
func (s *DockerCLIEventSuite) TestEventsOOMDisableTrue(c *testing.T) {
testRequires(c, DaemonIsLinux, oomControl, memoryLimitSupport, swapMemorySupport, NotPpc64le)
skip.If(c, GitHubActions, "FIXME: https://github.com/moby/moby/pull/36541")
errChan := make(chan error, 1)
observer, err := newEventObserver(c)
assert.NilError(c, err)
err = observer.Start()
assert.NilError(c, err)
defer observer.Stop()
go func() {
defer close(errChan)
out, exitCode, _ := dockerCmdWithError("run", "--oom-kill-disable=true", "--name", "oomTrue", "-m", "10MB", "busybox", "sh", "-c", "x=a; while true; do x=$x$x$x$x; done")
if expected := 137; exitCode != expected {
errChan <- fmt.Errorf("wrong exit code for OOM container: expected %d, got %d (output: %q)", expected, exitCode, out)
}
}()
cli.WaitRun(c, "oomTrue")
defer dockerCmdWithResult("kill", "oomTrue")
containerID := inspectField(c, "oomTrue", "Id")
testActions := map[string]chan bool{
"oom": make(chan bool),
}
matcher := matchEventLine(containerID, "container", testActions)
processor := processEventMatch(testActions)
go observer.Match(matcher, processor)
select {
case <-time.After(20 * time.Second):
observer.CheckEventError(c, containerID, "oom", matcher)
case <-testActions["oom"]:
// ignore, done
case errRun := <-errChan:
if errRun != nil {
c.Fatalf("%v", errRun)
} else {
c.Fatalf("container should be still running but it's not")
}
}
status := inspectField(c, "oomTrue", "State.Status")
assert.Equal(c, strings.TrimSpace(status), "running", "container should be still running")
}
// #18453
func (s *DockerCLIEventSuite) TestEventsContainerFilterByName(c *testing.T) {
testRequires(c, DaemonIsLinux)
cOut := cli.DockerCmd(c, "run", "--name=foo", "-d", "busybox", "top").Stdout()
c1 := strings.TrimSpace(cOut)
cli.WaitRun(c, "foo")
cOut = cli.DockerCmd(c, "run", "--name=bar", "-d", "busybox", "top").Stdout()
c2 := strings.TrimSpace(cOut)
cli.WaitRun(c, "bar")
out := cli.DockerCmd(c, "events", "-f", "container=foo", "--since=0", "--until", daemonUnixTime(c)).Stdout()
assert.Assert(c, strings.Contains(out, c1), out)
assert.Assert(c, !strings.Contains(out, c2), out)
}
// #18453
func (s *DockerCLIEventSuite) TestEventsContainerFilterBeforeCreate(c *testing.T) {
testRequires(c, DaemonIsLinux)
buf := &bytes.Buffer{}
cmd := exec.Command(dockerBinary, "events", "-f", "container=foo", "--since=0")
cmd.Stdout = buf
assert.NilError(c, cmd.Start())
defer cmd.Wait()
defer cmd.Process.Kill()
// Sleep for a second to make sure we are testing the case where events are listened before container starts.
time.Sleep(time.Second)
id := cli.DockerCmd(c, "run", "--name=foo", "-d", "busybox", "top").Stdout()
cID := strings.TrimSpace(id)
for i := 0; ; i++ {
out := buf.String()
if strings.Contains(out, cID) {
break
}
if i > 30 {
c.Fatalf("Missing event of container (foo, %v), got %q", cID, out)
}
time.Sleep(500 * time.Millisecond)
}
}
func (s *DockerCLIEventSuite) TestVolumeEvents(c *testing.T) {
testRequires(c, DaemonIsLinux)
since := daemonUnixTime(c)
// Observe create/mount volume actions
cli.DockerCmd(c, "volume", "create", "test-event-volume-local")
cli.DockerCmd(c, "run", "--name", "test-volume-container", "--volume", "test-event-volume-local:/foo", "-d", "busybox", "true")
// Observe unmount/destroy volume actions
cli.DockerCmd(c, "rm", "-f", "test-volume-container")
cli.DockerCmd(c, "volume", "rm", "test-event-volume-local")
until := daemonUnixTime(c)
out := cli.DockerCmd(c, "events", "--since", since, "--until", until).Stdout()
events := strings.Split(strings.TrimSpace(out), "\n")
assert.Assert(c, len(events) > 3)
volumeEvents := eventActionsByIDAndType(c, events, "test-event-volume-local", "volume")
assert.Equal(c, len(volumeEvents), 4)
assert.Equal(c, volumeEvents[0], "create")
assert.Equal(c, volumeEvents[1], "mount")
assert.Equal(c, volumeEvents[2], "unmount")
assert.Equal(c, volumeEvents[3], "destroy")
}
func (s *DockerCLIEventSuite) TestNetworkEvents(c *testing.T) {
testRequires(c, DaemonIsLinux)
since := daemonUnixTime(c)
// Observe create/connect network actions
cli.DockerCmd(c, "network", "create", "test-event-network-local")
cli.DockerCmd(c, "run", "--name", "test-network-container", "--net", "test-event-network-local", "-d", "busybox", "true")
// Observe disconnect/destroy network actions
cli.DockerCmd(c, "rm", "-f", "test-network-container")
cli.DockerCmd(c, "network", "rm", "test-event-network-local")
until := daemonUnixTime(c)
out := cli.DockerCmd(c, "events", "--since", since, "--until", until).Stdout()
events := strings.Split(strings.TrimSpace(out), "\n")
assert.Assert(c, len(events) > 4)
netEvents := eventActionsByIDAndType(c, events, "test-event-network-local", "network")
assert.Equal(c, len(netEvents), 4)
assert.Equal(c, netEvents[0], "create")
assert.Equal(c, netEvents[1], "connect")
assert.Equal(c, netEvents[2], "disconnect")
assert.Equal(c, netEvents[3], "destroy")
}
func (s *DockerCLIEventSuite) TestEventsContainerWithMultiNetwork(c *testing.T) {
testRequires(c, DaemonIsLinux)
// Observe create/connect network actions
cli.DockerCmd(c, "network", "create", "test-event-network-local-1")
cli.DockerCmd(c, "network", "create", "test-event-network-local-2")
cli.DockerCmd(c, "run", "--name", "test-network-container", "--net", "test-event-network-local-1", "-td", "busybox", "sh")
cli.WaitRun(c, "test-network-container")
cli.DockerCmd(c, "network", "connect", "test-event-network-local-2", "test-network-container")
since := daemonUnixTime(c)
cli.DockerCmd(c, "stop", "-t", "1", "test-network-container")
until := daemonUnixTime(c)
out := cli.DockerCmd(c, "events", "--since", since, "--until", until, "-f", "type=network").Stdout()
netEvents := strings.Split(strings.TrimSpace(out), "\n")
// received two network disconnect events
assert.Equal(c, len(netEvents), 2)
assert.Assert(c, is.Contains(netEvents[0], "disconnect"))
assert.Assert(c, is.Contains(netEvents[1], "disconnect"))
// both networks appeared in the network event output
assert.Assert(c, is.Contains(out, "test-event-network-local-1"))
assert.Assert(c, is.Contains(out, "test-event-network-local-2"))
}
func (s *DockerCLIEventSuite) TestEventsStreaming(c *testing.T) {
testRequires(c, DaemonIsLinux)
observer, err := newEventObserver(c)
assert.NilError(c, err)
err = observer.Start()
assert.NilError(c, err)
defer observer.Stop()
out := cli.DockerCmd(c, "run", "-d", "busybox:latest", "true").Stdout()
containerID := strings.TrimSpace(out)
testActions := map[string]chan bool{
"create": make(chan bool, 1),
"start": make(chan bool, 1),
"die": make(chan bool, 1),
"destroy": make(chan bool, 1),
}
matcher := matchEventLine(containerID, "container", testActions)
processor := processEventMatch(testActions)
go observer.Match(matcher, processor)
select {
case <-time.After(5 * time.Second):
observer.CheckEventError(c, containerID, "create", matcher)
case <-testActions["create"]:
// ignore, done
}
select {
case <-time.After(5 * time.Second):
observer.CheckEventError(c, containerID, "start", matcher)
case <-testActions["start"]:
// ignore, done
}
select {
case <-time.After(5 * time.Second):
observer.CheckEventError(c, containerID, "die", matcher)
case <-testActions["die"]:
// ignore, done
}
cli.DockerCmd(c, "rm", containerID)
select {
case <-time.After(5 * time.Second):
observer.CheckEventError(c, containerID, "destroy", matcher)
case <-testActions["destroy"]:
// ignore, done
}
}
func (s *DockerCLIEventSuite) TestEventsImageUntagDelete(c *testing.T) {
testRequires(c, DaemonIsLinux)
observer, err := newEventObserver(c)
assert.NilError(c, err)
err = observer.Start()
assert.NilError(c, err)
defer observer.Stop()
name := "testimageevents"
buildImageSuccessfully(c, name, build.WithDockerfile(`FROM scratch
MAINTAINER "docker"`))
imageID := getIDByName(c, name)
assert.NilError(c, deleteImages(name))
testActions := map[string]chan bool{
"untag": make(chan bool, 1),
"delete": make(chan bool, 1),
}
matcher := matchEventLine(imageID, "image", testActions)
processor := processEventMatch(testActions)
go observer.Match(matcher, processor)
select {
case <-time.After(10 * time.Second):
observer.CheckEventError(c, imageID, "untag", matcher)
case <-testActions["untag"]:
// ignore, done
}
select {
case <-time.After(10 * time.Second):
observer.CheckEventError(c, imageID, "delete", matcher)
case <-testActions["delete"]:
// ignore, done
}
}
func (s *DockerCLIEventSuite) TestEventsFilterVolumeAndNetworkType(c *testing.T) {
testRequires(c, DaemonIsLinux)
since := daemonUnixTime(c)
cli.DockerCmd(c, "network", "create", "test-event-network-type")
cli.DockerCmd(c, "volume", "create", "test-event-volume-type")
out := cli.DockerCmd(c, "events", "--filter", "type=volume", "--filter", "type=network", "--since", since, "--until", daemonUnixTime(c)).Stdout()
events := strings.Split(strings.TrimSpace(out), "\n")
assert.Assert(c, len(events) >= 2, out)
networkActions := eventActionsByIDAndType(c, events, "test-event-network-type", "network")
volumeActions := eventActionsByIDAndType(c, events, "test-event-volume-type", "volume")
assert.Equal(c, volumeActions[0], "create")
assert.Equal(c, networkActions[0], "create")
}
func (s *DockerCLIEventSuite) TestEventsFilterVolumeID(c *testing.T) {
testRequires(c, DaemonIsLinux)
since := daemonUnixTime(c)
cli.DockerCmd(c, "volume", "create", "test-event-volume-id")
out := cli.DockerCmd(c, "events", "--filter", "volume=test-event-volume-id", "--since", since, "--until", daemonUnixTime(c)).Stdout()
events := strings.Split(strings.TrimSpace(out), "\n")
assert.Equal(c, len(events), 1)
assert.Equal(c, len(events), 1)
assert.Assert(c, is.Contains(events[0], "test-event-volume-id"))
assert.Assert(c, is.Contains(events[0], "driver=local"))
}
func (s *DockerCLIEventSuite) TestEventsFilterNetworkID(c *testing.T) {
testRequires(c, DaemonIsLinux)
since := daemonUnixTime(c)
cli.DockerCmd(c, "network", "create", "test-event-network-local")
out := cli.DockerCmd(c, "events", "--filter", "network=test-event-network-local", "--since", since, "--until", daemonUnixTime(c)).Stdout()
events := strings.Split(strings.TrimSpace(out), "\n")
assert.Equal(c, len(events), 1)
assert.Assert(c, is.Contains(events[0], "test-event-network-local"))
assert.Assert(c, is.Contains(events[0], "type=bridge"))
}
func (s *DockerDaemonSuite) TestDaemonEvents(c *testing.T) {
// daemon config file
configFilePath := "test.json"
defer os.Remove(configFilePath)
daemonConfig := `{"labels":["foo=bar"]}`
err := os.WriteFile(configFilePath, []byte(daemonConfig), 0o644)
assert.NilError(c, err)
s.d.Start(c, "--config-file="+configFilePath)
info := s.d.Info(c)
daemonConfig = `{"max-concurrent-downloads":1,"labels":["bar=foo"], "shutdown-timeout": 10}`
err = os.WriteFile(configFilePath, []byte(daemonConfig), 0o644)
assert.NilError(c, err)
assert.NilError(c, s.d.Signal(unix.SIGHUP))
time.Sleep(3 * time.Second)
out, err := s.d.Cmd("events", "--since=0", "--until", daemonUnixTime(c))
assert.NilError(c, err)
// only check for values known (daemon ID/name) or explicitly set above,
// otherwise just check for names being present.
expectedSubstrings := []string{
` daemon reload ` + info.ID + " ",
`debug=true, `,
` default-ipc-mode=`,
` default-runtime=`,
` default-shm-size=`,
` insecure-registries=[`,
` labels=["bar=foo"], `,
` live-restore=`,
` max-concurrent-downloads=1, `,
` max-concurrent-uploads=5, `,
` name=` + info.Name,
` registry-mirrors=[`,
` runtimes=`,
` shutdown-timeout=10)`,
}
for _, s := range expectedSubstrings {
assert.Check(c, is.Contains(out, s))
}
}
func (s *DockerDaemonSuite) TestDaemonEventsWithFilters(c *testing.T) {
// daemon config file
configFilePath := "test.json"
defer os.Remove(configFilePath)
daemonConfig := `{"labels":["foo=bar"]}`
err := os.WriteFile(configFilePath, []byte(daemonConfig), 0o644)
assert.NilError(c, err)
s.d.Start(c, "--config-file="+configFilePath)
info := s.d.Info(c)
assert.NilError(c, s.d.Signal(unix.SIGHUP))
time.Sleep(3 * time.Second)
out, err := s.d.Cmd("events", "--since=0", "--until", daemonUnixTime(c), "--filter", fmt.Sprintf("daemon=%s", info.ID))
assert.NilError(c, err)
assert.Assert(c, is.Contains(out, fmt.Sprintf("daemon reload %s", info.ID)))
out, err = s.d.Cmd("events", "--since=0", "--until", daemonUnixTime(c), "--filter", fmt.Sprintf("daemon=%s", info.ID))
assert.NilError(c, err)
assert.Assert(c, is.Contains(out, fmt.Sprintf("daemon reload %s", info.ID)))
out, err = s.d.Cmd("events", "--since=0", "--until", daemonUnixTime(c), "--filter", "daemon=foo")
assert.NilError(c, err)
assert.Assert(c, !strings.Contains(out, fmt.Sprintf("daemon reload %s", info.ID)))
out, err = s.d.Cmd("events", "--since=0", "--until", daemonUnixTime(c), "--filter", "type=daemon")
assert.NilError(c, err)
assert.Assert(c, is.Contains(out, fmt.Sprintf("daemon reload %s", info.ID)))
out, err = s.d.Cmd("events", "--since=0", "--until", daemonUnixTime(c), "--filter", "type=container")
assert.NilError(c, err)
assert.Assert(c, !strings.Contains(out, fmt.Sprintf("daemon reload %s", info.ID)))
}