github.com/khulnasoft/cli@v0.0.0-20240402070845-01bcad7beefa/cli/command/system/events.go (about) 1 package system 2 3 import ( 4 "context" 5 "fmt" 6 "io" 7 "sort" 8 "strings" 9 "text/template" 10 "time" 11 12 "github.com/docker/docker/api/types" 13 "github.com/docker/docker/api/types/events" 14 "github.com/khulnasoft/cli/cli" 15 "github.com/khulnasoft/cli/cli/command" 16 "github.com/khulnasoft/cli/cli/command/completion" 17 "github.com/khulnasoft/cli/cli/command/formatter" 18 flagsHelper "github.com/khulnasoft/cli/cli/flags" 19 "github.com/khulnasoft/cli/opts" 20 "github.com/khulnasoft/cli/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.Cli) *cobra.Command { 33 options := 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(cmd.Context(), dockerCli, &options) 41 }, 42 Annotations: map[string]string{ 43 "aliases": "docker system events, docker events", 44 }, 45 ValidArgsFunction: completion.NoComplete, 46 } 47 48 flags := cmd.Flags() 49 flags.StringVar(&options.since, "since", "", "Show all events created since timestamp") 50 flags.StringVar(&options.until, "until", "", "Stream events until this timestamp") 51 flags.VarP(&options.filter, "filter", "f", "Filter output based on conditions provided") 52 flags.StringVar(&options.format, "format", "", flagsHelper.InspectFormatHelp) // using the same flag description as "inspect" commands for now. 53 54 return cmd 55 } 56 57 func runEvents(ctx context.Context, dockerCli command.Cli, options *eventsOptions) error { 58 tmpl, err := makeTemplate(options.format) 59 if err != nil { 60 return cli.StatusError{ 61 StatusCode: 64, 62 Status: "Error parsing format: " + err.Error(), 63 } 64 } 65 ctx, cancel := context.WithCancel(ctx) 66 evts, errs := dockerCli.Client().Events(ctx, types.EventsOptions{ 67 Since: options.since, 68 Until: options.until, 69 Filters: options.filter.Value(), 70 }) 71 defer cancel() 72 73 out := dockerCli.Out() 74 75 for { 76 select { 77 case event := <-evts: 78 if err := handleEvent(out, event, tmpl); err != nil { 79 return err 80 } 81 case err := <-errs: 82 if err == io.EOF { 83 return nil 84 } 85 return err 86 } 87 } 88 } 89 90 func handleEvent(out io.Writer, event events.Message, tmpl *template.Template) error { 91 if tmpl == nil { 92 return prettyPrintEvent(out, event) 93 } 94 95 return formatEvent(out, event, tmpl) 96 } 97 98 func makeTemplate(format string) (*template.Template, error) { 99 switch format { 100 case "": 101 return nil, nil 102 case formatter.JSONFormatKey: 103 format = formatter.JSONFormat 104 } 105 tmpl, err := templates.Parse(format) 106 if err != nil { 107 return tmpl, err 108 } 109 // execute the template on an empty message to validate a bad 110 // template like "{{.badFieldString}}" 111 return tmpl, tmpl.Execute(io.Discard, &events.Message{}) 112 } 113 114 // rfc3339NanoFixed is similar to time.RFC3339Nano, except it pads nanoseconds 115 // zeros to maintain a fixed number of characters 116 const rfc3339NanoFixed = "2006-01-02T15:04:05.000000000Z07:00" 117 118 // prettyPrintEvent prints all types of event information. 119 // Each output includes the event type, actor id, name and action. 120 // Actor attributes are printed at the end if the actor has any. 121 func prettyPrintEvent(out io.Writer, event events.Message) error { 122 if event.TimeNano != 0 { 123 fmt.Fprintf(out, "%s ", time.Unix(0, event.TimeNano).Format(rfc3339NanoFixed)) 124 } else if event.Time != 0 { 125 fmt.Fprintf(out, "%s ", time.Unix(event.Time, 0).Format(rfc3339NanoFixed)) 126 } 127 128 fmt.Fprintf(out, "%s %s %s", event.Type, event.Action, event.Actor.ID) 129 130 if len(event.Actor.Attributes) > 0 { 131 var attrs []string 132 var keys []string 133 for k := range event.Actor.Attributes { 134 keys = append(keys, k) 135 } 136 sort.Strings(keys) 137 for _, k := range keys { 138 v := event.Actor.Attributes[k] 139 attrs = append(attrs, k+"="+v) 140 } 141 fmt.Fprintf(out, " (%s)", strings.Join(attrs, ", ")) 142 } 143 fmt.Fprint(out, "\n") 144 return nil 145 } 146 147 func formatEvent(out io.Writer, event events.Message, tmpl *template.Template) error { 148 defer out.Write([]byte{'\n'}) 149 return tmpl.Execute(out, event) 150 }