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