// Events returns a stream of events in the daemon in a ReadCloser. // It's up to the caller to close the stream. func (cli *Client) Events(ctx context.Context, options types.EventsOptions) (io.ReadCloser, error) { query := url.Values{} ref := time.Now() if options.Since != "" { ts, err := timetypes.GetTimestamp(options.Since, ref) if err != nil { return nil, err } query.Set("since", ts) } if options.Until != "" { ts, err := timetypes.GetTimestamp(options.Until, ref) if err != nil { return nil, err } query.Set("until", ts) } if options.Filters.Len() > 0 { filterJSON, err := filters.ToParamWithVersion(cli.version, options.Filters) if err != nil { return nil, err } query.Set("filters", filterJSON) } serverResponse, err := cli.get(ctx, "/events", query, nil) if err != nil { return nil, err } return serverResponse.body, nil }
func TestLoadBufferedEventsOnlyFromPast(t *testing.T) { now := time.Now() f, err := timetypes.GetTimestamp("2016-03-07T17:28:03.090000000+02:00", now) if err != nil { t.Fatal(err) } s, sNano, err := timetypes.ParseTimestamps(f, 0) if err != nil { t.Fatal(err) } f, err = timetypes.GetTimestamp("2016-03-07T17:28:03.100000000+02:00", now) if err != nil { t.Fatal(err) } u, uNano, err := timetypes.ParseTimestamps(f, 0) if err != nil { t.Fatal(err) } m1, err := eventstestutils.Scan("2016-03-07T17:28:03.022433271+02:00 container die 0b863f2a26c18557fc6cdadda007c459f9ec81b874780808138aea78a3595079 (image=ubuntu, name=small_hoover)") if err != nil { t.Fatal(err) } m2, err := eventstestutils.Scan("2016-03-07T17:28:03.091719377+02:00 network disconnect 19c5ed41acb798f26b751e0035cd7821741ab79e2bbd59a66b5fd8abf954eaa0 (type=bridge, container=0b863f2a26c18557fc6cdadda007c459f9ec81b874780808138aea78a3595079, name=bridge)") if err != nil { t.Fatal(err) } m3, err := eventstestutils.Scan("2016-03-07T17:28:03.129014751+02:00 container destroy 0b863f2a26c18557fc6cdadda007c459f9ec81b874780808138aea78a3595079 (image=ubuntu, name=small_hoover)") if err != nil { t.Fatal(err) } events := &Events{ events: []events.Message{*m1, *m2, *m3}, } since := time.Unix(s, sNano) until := time.Unix(u, uNano) out := events.loadBufferedEvents(since, until, nil) if len(out) != 1 { t.Fatalf("expected 1 message, got %d: %v", len(out), out) } if out[0].Type != "network" { t.Fatalf("expected network event, got %s", out[0].Type) } }
// ContainerLogs returns the logs generated by a container in an io.ReadCloser. // It's up to the caller to close the stream. func (cli *Client) ContainerLogs(ctx context.Context, options types.ContainerLogsOptions) (io.ReadCloser, error) { query := url.Values{} if options.ShowStdout { query.Set("stdout", "1") } if options.ShowStderr { query.Set("stderr", "1") } if options.Since != "" { ts, err := timetypes.GetTimestamp(options.Since, time.Now()) if err != nil { return nil, err } query.Set("since", ts) } if options.Timestamps { query.Set("timestamps", "1") } if options.Follow { query.Set("follow", "1") } query.Set("tail", options.Tail) resp, err := cli.get(ctx, "/containers/"+options.ContainerID+"/logs", query, nil) if err != nil { return nil, err } return resp.body, nil }
// Scan turns an event string like the default ones formatted in the cli output // and turns it into an event message. func Scan(text string) (*events.Message, error) { md := ScanMap(text) if len(md) == 0 { return nil, fmt.Errorf("text is not an event: %s", text) } f, err := timetypes.GetTimestamp(md["timestamp"], time.Now()) if err != nil { return nil, err } t, tn, err := timetypes.ParseTimestamps(f, -1) if err != nil { return nil, err } attrs := make(map[string]string) for _, a := range strings.SplitN(md["attributes"], ", ", -1) { kv := strings.SplitN(a, "=", 2) attrs[kv[0]] = kv[1] } tu := time.Unix(t, tn) return &events.Message{ Time: t, TimeNano: tu.UnixNano(), Type: md["eventType"], Action: md["action"], Actor: events.Actor{ ID: md["id"], Attributes: attrs, }, }, nil }