github.com/vieux/docker@v0.6.3-0.20161004191708-e097c2a938c7/cli/command/system/events.go (about) 1 package system 2 3 import ( 4 "fmt" 5 "io" 6 "io/ioutil" 7 "sort" 8 "strings" 9 "text/template" 10 "time" 11 12 "golang.org/x/net/context" 13 14 "github.com/docker/docker/api/types" 15 eventtypes "github.com/docker/docker/api/types/events" 16 "github.com/docker/docker/cli" 17 "github.com/docker/docker/cli/command" 18 "github.com/docker/docker/opts" 19 "github.com/docker/docker/pkg/jsonlog" 20 "github.com/docker/docker/utils/templates" 21 "github.com/spf13/cobra" 22 ) 23 24 type eventsOptions struct { 25 since string 26 until string 27 filter opts.FilterOpt 28 format string 29 } 30 31 // NewEventsCommand creates a new cobra.Command for `docker events` 32 func NewEventsCommand(dockerCli *command.DockerCli) *cobra.Command { 33 opts := eventsOptions{filter: opts.NewFilterOpt()} 34 35 cmd := &cobra.Command{ 36 Use: "events [OPTIONS]", 37 Short: "Get real time events from the server", 38 Args: cli.NoArgs, 39 RunE: func(cmd *cobra.Command, args []string) error { 40 return runEvents(dockerCli, &opts) 41 }, 42 } 43 44 flags := cmd.Flags() 45 flags.StringVar(&opts.since, "since", "", "Show all events created since timestamp") 46 flags.StringVar(&opts.until, "until", "", "Stream events until this timestamp") 47 flags.VarP(&opts.filter, "filter", "f", "Filter output based on conditions provided") 48 flags.StringVar(&opts.format, "format", "", "Format the output using the given go template") 49 50 return cmd 51 } 52 53 func runEvents(dockerCli *command.DockerCli, opts *eventsOptions) error { 54 tmpl, err := makeTemplate(opts.format) 55 if err != nil { 56 return cli.StatusError{ 57 StatusCode: 64, 58 Status: "Error parsing format: " + err.Error()} 59 } 60 options := types.EventsOptions{ 61 Since: opts.since, 62 Until: opts.until, 63 Filters: opts.filter.Value(), 64 } 65 66 ctx, cancel := context.WithCancel(context.Background()) 67 events, errs := dockerCli.Client().Events(ctx, options) 68 defer cancel() 69 70 out := dockerCli.Out() 71 72 for { 73 select { 74 case event := <-events: 75 if err := handleEvent(out, event, tmpl); err != nil { 76 return err 77 } 78 case err := <-errs: 79 if err == io.EOF { 80 return nil 81 } 82 return err 83 } 84 } 85 } 86 87 func handleEvent(out io.Writer, event eventtypes.Message, tmpl *template.Template) error { 88 if tmpl == nil { 89 return prettyPrintEvent(out, event) 90 } 91 92 return formatEvent(out, event, tmpl) 93 } 94 95 func makeTemplate(format string) (*template.Template, error) { 96 if format == "" { 97 return nil, nil 98 } 99 tmpl, err := templates.Parse(format) 100 if err != nil { 101 return tmpl, err 102 } 103 // we execute the template for an empty message, so as to validate 104 // a bad template like "{{.badFieldString}}" 105 return tmpl, tmpl.Execute(ioutil.Discard, &eventtypes.Message{}) 106 } 107 108 // prettyPrintEvent prints all types of event information. 109 // Each output includes the event type, actor id, name and action. 110 // Actor attributes are printed at the end if the actor has any. 111 func prettyPrintEvent(out io.Writer, event eventtypes.Message) error { 112 if event.TimeNano != 0 { 113 fmt.Fprintf(out, "%s ", time.Unix(0, event.TimeNano).Format(jsonlog.RFC3339NanoFixed)) 114 } else if event.Time != 0 { 115 fmt.Fprintf(out, "%s ", time.Unix(event.Time, 0).Format(jsonlog.RFC3339NanoFixed)) 116 } 117 118 fmt.Fprintf(out, "%s %s %s", event.Type, event.Action, event.Actor.ID) 119 120 if len(event.Actor.Attributes) > 0 { 121 var attrs []string 122 var keys []string 123 for k := range event.Actor.Attributes { 124 keys = append(keys, k) 125 } 126 sort.Strings(keys) 127 for _, k := range keys { 128 v := event.Actor.Attributes[k] 129 attrs = append(attrs, fmt.Sprintf("%s=%s", k, v)) 130 } 131 fmt.Fprintf(out, " (%s)", strings.Join(attrs, ", ")) 132 } 133 fmt.Fprint(out, "\n") 134 return nil 135 } 136 137 func formatEvent(out io.Writer, event eventtypes.Message, tmpl *template.Template) error { 138 defer out.Write([]byte{'\n'}) 139 return tmpl.Execute(out, event) 140 }