github.com/jfrazelle/docker@v1.1.2-0.20210712172922-bf78e25fe508/integration/system/event_test.go (about)

     1  package system // import "github.com/docker/docker/integration/system"
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"errors"
     7  	"io"
     8  	"net/http"
     9  	"net/url"
    10  	"strconv"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/docker/docker/api/types"
    15  	"github.com/docker/docker/api/types/events"
    16  	"github.com/docker/docker/api/types/filters"
    17  	"github.com/docker/docker/api/types/mount"
    18  	"github.com/docker/docker/api/types/strslice"
    19  	"github.com/docker/docker/api/types/versions"
    20  	"github.com/docker/docker/api/types/volume"
    21  	"github.com/docker/docker/integration/internal/container"
    22  	"github.com/docker/docker/pkg/jsonmessage"
    23  	"github.com/docker/docker/testutil/request"
    24  	req "github.com/docker/docker/testutil/request"
    25  	"gotest.tools/v3/assert"
    26  	is "gotest.tools/v3/assert/cmp"
    27  	"gotest.tools/v3/skip"
    28  )
    29  
    30  func TestEventsExecDie(t *testing.T) {
    31  	skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.36"), "broken in earlier versions")
    32  	skip.If(t, testEnv.OSType == "windows", "FIXME. Suspect may need to wait until container is running before exec")
    33  	defer setupTest(t)()
    34  	ctx := context.Background()
    35  	client := testEnv.APIClient()
    36  
    37  	cID := container.Run(ctx, t, client)
    38  
    39  	id, err := client.ContainerExecCreate(ctx, cID,
    40  		types.ExecConfig{
    41  			Cmd: strslice.StrSlice([]string{"echo", "hello"}),
    42  		},
    43  	)
    44  	assert.NilError(t, err)
    45  
    46  	filters := filters.NewArgs(
    47  		filters.Arg("container", cID),
    48  		filters.Arg("event", "exec_die"),
    49  	)
    50  	msg, errors := client.Events(ctx, types.EventsOptions{
    51  		Filters: filters,
    52  	})
    53  
    54  	err = client.ContainerExecStart(ctx, id.ID,
    55  		types.ExecStartCheck{
    56  			Detach: true,
    57  			Tty:    false,
    58  		},
    59  	)
    60  	assert.NilError(t, err)
    61  
    62  	select {
    63  	case m := <-msg:
    64  		assert.Equal(t, m.Type, "container")
    65  		assert.Equal(t, m.Actor.ID, cID)
    66  		assert.Equal(t, m.Action, "exec_die")
    67  		assert.Equal(t, m.Actor.Attributes["execID"], id.ID)
    68  		assert.Equal(t, m.Actor.Attributes["exitCode"], "0")
    69  	case err = <-errors:
    70  		assert.NilError(t, err)
    71  	case <-time.After(time.Second * 3):
    72  		t.Fatal("timeout hit")
    73  	}
    74  
    75  }
    76  
    77  // Test case for #18888: Events messages have been switched from generic
    78  // `JSONMessage` to `events.Message` types. The switch does not break the
    79  // backward compatibility so old `JSONMessage` could still be used.
    80  // This test verifies that backward compatibility maintains.
    81  func TestEventsBackwardsCompatible(t *testing.T) {
    82  	skip.If(t, testEnv.OSType == "windows", "Windows doesn't support back-compat messages")
    83  	defer setupTest(t)()
    84  	ctx := context.Background()
    85  	client := testEnv.APIClient()
    86  
    87  	since := request.DaemonTime(ctx, t, client, testEnv)
    88  	ts := strconv.FormatInt(since.Unix(), 10)
    89  
    90  	cID := container.Create(ctx, t, client)
    91  
    92  	// In case there is no events, the API should have responded immediately (not blocking),
    93  	// The test here makes sure the response time is less than 3 sec.
    94  	expectedTime := time.Now().Add(3 * time.Second)
    95  	emptyResp, emptyBody, err := req.Get("/events")
    96  	assert.NilError(t, err)
    97  	defer emptyBody.Close()
    98  	assert.Check(t, is.DeepEqual(http.StatusOK, emptyResp.StatusCode))
    99  	assert.Check(t, time.Now().Before(expectedTime), "timeout waiting for events api to respond, should have responded immediately")
   100  
   101  	// We also test to make sure the `events.Message` is compatible with `JSONMessage`
   102  	q := url.Values{}
   103  	q.Set("since", ts)
   104  	_, body, err := req.Get("/events?" + q.Encode())
   105  	assert.NilError(t, err)
   106  	defer body.Close()
   107  
   108  	dec := json.NewDecoder(body)
   109  	var containerCreateEvent *jsonmessage.JSONMessage
   110  	for {
   111  		var event jsonmessage.JSONMessage
   112  		if err := dec.Decode(&event); err != nil {
   113  			if err == io.EOF {
   114  				break
   115  			}
   116  			assert.NilError(t, err)
   117  		}
   118  		if event.Status == "create" && event.ID == cID {
   119  			containerCreateEvent = &event
   120  			break
   121  		}
   122  	}
   123  
   124  	assert.Check(t, containerCreateEvent != nil)
   125  	assert.Check(t, is.Equal("create", containerCreateEvent.Status))
   126  	assert.Check(t, is.Equal(cID, containerCreateEvent.ID))
   127  	assert.Check(t, is.Equal("busybox", containerCreateEvent.From))
   128  }
   129  
   130  // TestEventsVolumeCreate verifies that volume create events are only fired
   131  // once: when creating the volume, and not when attaching to a container.
   132  func TestEventsVolumeCreate(t *testing.T) {
   133  	skip.If(t, testEnv.OSType == "windows", "FIXME: Windows doesn't trigger the events? Could be a race")
   134  
   135  	defer setupTest(t)()
   136  	ctx, cancel := context.WithCancel(context.Background())
   137  	defer cancel()
   138  
   139  	client := testEnv.APIClient()
   140  
   141  	since := request.DaemonUnixTime(ctx, t, client, testEnv)
   142  	volName := t.Name()
   143  	getEvents := func(messages <-chan events.Message, errs <-chan error) ([]events.Message, error) {
   144  		var evts []events.Message
   145  
   146  		for {
   147  			select {
   148  			case m := <-messages:
   149  				evts = append(evts, m)
   150  			case err := <-errs:
   151  				if err == io.EOF {
   152  					return evts, nil
   153  				}
   154  				return nil, err
   155  			case <-time.After(time.Second * 3):
   156  				return nil, errors.New("timeout hit")
   157  			}
   158  		}
   159  	}
   160  
   161  	_, err := client.VolumeCreate(ctx, volume.VolumeCreateBody{Name: volName})
   162  	assert.NilError(t, err)
   163  
   164  	filter := filters.NewArgs(
   165  		filters.Arg("type", "volume"),
   166  		filters.Arg("event", "create"),
   167  		filters.Arg("volume", volName),
   168  	)
   169  	messages, errs := client.Events(ctx, types.EventsOptions{
   170  		Since:   since,
   171  		Until:   request.DaemonUnixTime(ctx, t, client, testEnv),
   172  		Filters: filter,
   173  	})
   174  
   175  	volEvents, err := getEvents(messages, errs)
   176  	assert.NilError(t, err)
   177  	assert.Equal(t, len(volEvents), 1, "expected volume create event when creating a volume")
   178  
   179  	container.Create(ctx, t, client, container.WithMount(mount.Mount{
   180  		Type:   mount.TypeVolume,
   181  		Source: volName,
   182  		Target: "/tmp/foo",
   183  	}))
   184  
   185  	messages, errs = client.Events(ctx, types.EventsOptions{
   186  		Since:   since,
   187  		Until:   request.DaemonUnixTime(ctx, t, client, testEnv),
   188  		Filters: filter,
   189  	})
   190  
   191  	volEvents, err = getEvents(messages, errs)
   192  	assert.NilError(t, err)
   193  	assert.Equal(t, len(volEvents), 1, "expected volume create event to be fired only once")
   194  }