Allow duration strings as --since/--until

Fixes #13107. This change enables Go duration strings
computed relative to the client machine’s time to be used
as input parameters to `docker events --since/--until`
and `docker logs --since` arguments.

Added unit tests for pkg/timeutils.GetTimestamp as well.

Signed-off-by: Ahmet Alp Balkan <ahmetalpbalkan@gmail.com>
This commit is contained in:
Ahmet Alp Balkan 2015-05-12 19:59:34 +00:00
parent e4855eebf2
commit 4e3b21f99e
8 changed files with 65 additions and 12 deletions

View File

@ -2,6 +2,7 @@ package client
import ( import (
"net/url" "net/url"
"time"
"github.com/docker/docker/opts" "github.com/docker/docker/opts"
flag "github.com/docker/docker/pkg/mflag" flag "github.com/docker/docker/pkg/mflag"
@ -36,11 +37,12 @@ func (cli *DockerCli) CmdEvents(args ...string) error {
return err return err
} }
} }
ref := time.Now()
if *since != "" { if *since != "" {
v.Set("since", timeutils.GetTimestamp(*since)) v.Set("since", timeutils.GetTimestamp(*since, ref))
} }
if *until != "" { if *until != "" {
v.Set("until", timeutils.GetTimestamp(*until)) v.Set("until", timeutils.GetTimestamp(*until, ref))
} }
if len(eventFilterArgs) > 0 { if len(eventFilterArgs) > 0 {
filterJSON, err := filters.ToParam(eventFilterArgs) filterJSON, err := filters.ToParam(eventFilterArgs)

View File

@ -4,6 +4,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/url" "net/url"
"time"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
flag "github.com/docker/docker/pkg/mflag" flag "github.com/docker/docker/pkg/mflag"
@ -46,7 +47,7 @@ func (cli *DockerCli) CmdLogs(args ...string) error {
v.Set("stderr", "1") v.Set("stderr", "1")
if *since != "" { if *since != "" {
v.Set("since", timeutils.GetTimestamp(*since)) v.Set("since", timeutils.GetTimestamp(*since, time.Now()))
} }
if *times { if *times {

View File

@ -37,6 +37,10 @@ and Docker images will report:
**--until**="" **--until**=""
Stream events until this timestamp Stream events until this timestamp
You can specify `--since` and `--until` parameters as an RFC 3339 date,
a UNIX timestamp, or a Go duration string (e.g. `1m30s`, `3h`). Docker computes
the date relative to the client machines time.
# EXAMPLES # EXAMPLES
## Listening for Docker events ## Listening for Docker events
@ -63,6 +67,15 @@ Again the output container IDs have been shortened for the purposes of this docu
2015-01-28T20:25:45.000000000-08:00 c21f6c22ba27: (from whenry/testimage:latest) die 2015-01-28T20:25:45.000000000-08:00 c21f6c22ba27: (from whenry/testimage:latest) die
2015-01-28T20:25:46.000000000-08:00 c21f6c22ba27: (from whenry/testimage:latest) stop 2015-01-28T20:25:46.000000000-08:00 c21f6c22ba27: (from whenry/testimage:latest) stop
The following example outputs all events that were generated in the last 3 minutes,
relative to the current time on the client machine:
# docker events --since '3m'
2015-05-12T11:51:30.999999999Z07:00 4386fb97867d: (from ubuntu-1:14.04) die
2015-05-12T15:52:12.999999999Z07:00 4 4386fb97867d: (from ubuntu-1:14.04) stop
2015-05-12T15:53:45.999999999Z07:00 7805c1d35632: (from redis:2.8) die
2015-05-12T15:54:03.999999999Z07:00 7805c1d35632: (from redis:2.8) stop
# HISTORY # HISTORY
April 2014, Originally compiled by William Henry (whenry at redhat dot com) April 2014, Originally compiled by William Henry (whenry at redhat dot com)
based on docker.com source material and internal work. based on docker.com source material and internal work.

View File

@ -41,6 +41,12 @@ then continue streaming new output from the containers stdout and stderr.
**--tail**="all" **--tail**="all"
Output the specified number of lines at the end of logs (defaults to all logs) Output the specified number of lines at the end of logs (defaults to all logs)
The `--since` option shows only the container logs generated after
a given date. You can specify the date as an RFC 3339 date, a UNIX
timestamp, or a Go duration string (e.g. `1m30s`, `3h`). Docker computes
the date relative to the client machines time. You can combine
the `--since` option with either or both of the `--follow` or `--tail` options.
# HISTORY # HISTORY
April 2014, Originally compiled by William Henry (whenry at redhat dot com) April 2014, Originally compiled by William Henry (whenry at redhat dot com)
based on docker.com source material and internal work. based on docker.com source material and internal work.

View File

@ -1100,6 +1100,10 @@ and Docker images will report:
untag, delete untag, delete
The `--since` and `--until` parameters can be Unix timestamps, RFC3339
dates or Go duration strings (e.g. `10m`, `1h30m`) computed relative to
client machines time.
#### Filtering #### Filtering
The filtering flag (`-f` or `--filter`) format is of "key=value". If you would like to use The filtering flag (`-f` or `--filter`) format is of "key=value". If you would like to use
@ -1162,6 +1166,15 @@ You'll need two shells for this example.
2014-05-10T17:42:14.999999999Z07:00 7805c1d35632: (from redis:2.8) die 2014-05-10T17:42:14.999999999Z07:00 7805c1d35632: (from redis:2.8) die
2014-09-03T15:49:29.999999999Z07:00 7805c1d35632: (from redis:2.8) stop 2014-09-03T15:49:29.999999999Z07:00 7805c1d35632: (from redis:2.8) stop
This example outputs all events that were generated in the last 3 minutes,
relative to the current time on the client machine:
$ docker events --since '3m'
2015-05-12T11:51:30.999999999Z07:00 4386fb97867d: (from ubuntu-1:14.04) die
2015-05-12T15:52:12.999999999Z07:00 4 4386fb97867d: (from ubuntu-1:14.04) stop
2015-05-12T15:53:45.999999999Z07:00 7805c1d35632: (from redis:2.8) die
2015-05-12T15:54:03.999999999Z07:00 7805c1d35632: (from redis:2.8) stop
**Filter events:** **Filter events:**
$ docker events --filter 'event=stop' $ docker events --filter 'event=stop'
@ -1655,9 +1668,11 @@ timestamp, for example `2014-09-16T06:17:46.000000000Z`, to each
log entry. To ensure that the timestamps for are aligned the log entry. To ensure that the timestamps for are aligned the
nano-second part of the timestamp will be padded with zero when necessary. nano-second part of the timestamp will be padded with zero when necessary.
The `--since` option shows logs of a container generated only after The `--since` option shows only the container logs generated after
the given date, specified as RFC 3339 or UNIX timestamp. The `--since` option a given date. You can specify the date as an RFC 3339 date, a UNIX
can be combined with the `--follow` and `--tail` options. timestamp, or a Go duration string (e.g. `1m30s`, `3h`). Docker computes
the date relative to the client machines time. You can combine
the `--since` option with either or both of the `--follow` or `--tail` options.
## pause ## pause

View File

@ -28,9 +28,10 @@ func (s *DockerSuite) TestEventsTimestampFormats(c *check.C) {
// List of available time formats to --since // List of available time formats to --since
unixTs := func(t time.Time) string { return fmt.Sprintf("%v", t.Unix()) } unixTs := func(t time.Time) string { return fmt.Sprintf("%v", t.Unix()) }
rfc3339 := func(t time.Time) string { return t.Format(time.RFC3339) } rfc3339 := func(t time.Time) string { return t.Format(time.RFC3339) }
duration := func(t time.Time) string { return time.Now().Sub(t).String() }
// --since=$start must contain only the 'untag' event // --since=$start must contain only the 'untag' event
for _, f := range []func(time.Time) string{unixTs, rfc3339} { for _, f := range []func(time.Time) string{unixTs, rfc3339, duration} {
since, until := f(start), f(end) since, until := f(start), f(end)
cmd := exec.Command(dockerBinary, "events", "--since="+since, "--until="+until) cmd := exec.Command(dockerBinary, "events", "--since="+since, "--until="+until)
out, _, err := runCommandWithOutput(cmd) out, _, err := runCommandWithOutput(cmd)

View File

@ -6,10 +6,17 @@ import (
"time" "time"
) )
// GetTimestamp tries to parse given string as RFC3339 time // GetTimestamp tries to parse given string as golang duration,
// or Unix timestamp (with seconds precision), if successful // then RFC3339 time and finally as a Unix timestamp. If
//returns a Unix timestamp as string otherwise returns value back. // any of these were successful, it returns a Unix timestamp
func GetTimestamp(value string) string { // as string otherwise returns the given value back.
// In case of duration input, the returned timestamp is computed
// as the given reference time minus the amount of the duration.
func GetTimestamp(value string, reference time.Time) string {
if d, err := time.ParseDuration(value); value != "0" && err == nil {
return strconv.FormatInt(reference.Add(-d).Unix(), 10)
}
var format string var format string
if strings.Contains(value, ".") { if strings.Contains(value, ".") {
format = time.RFC3339Nano format = time.RFC3339Nano

View File

@ -1,10 +1,13 @@
package timeutils package timeutils
import ( import (
"fmt"
"testing" "testing"
"time"
) )
func TestGetTimestamp(t *testing.T) { func TestGetTimestamp(t *testing.T) {
now := time.Now()
cases := []struct{ in, expected string }{ cases := []struct{ in, expected string }{
{"0", "-62167305600"}, // 0 gets parsed year 0 {"0", "-62167305600"}, // 0 gets parsed year 0
@ -23,12 +26,17 @@ func TestGetTimestamp(t *testing.T) {
// unix timestamps returned as is // unix timestamps returned as is
{"1136073600", "1136073600"}, {"1136073600", "1136073600"},
// Durations
{"1m", fmt.Sprintf("%d", now.Add(-1*time.Minute).Unix())},
{"1.5h", fmt.Sprintf("%d", now.Add(-90*time.Minute).Unix())},
{"1h30m", fmt.Sprintf("%d", now.Add(-90*time.Minute).Unix())},
// String fallback // String fallback
{"invalid", "invalid"}, {"invalid", "invalid"},
} }
for _, c := range cases { for _, c := range cases {
o := GetTimestamp(c.in) o := GetTimestamp(c.in, now)
if o != c.expected { if o != c.expected {
t.Fatalf("wrong value for '%s'. expected:'%s' got:'%s'", c.in, c.expected, o) t.Fatalf("wrong value for '%s'. expected:'%s' got:'%s'", c.in, c.expected, o)
} }