gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/runsc/cmd/events.go (about)

     1  // Copyright 2018 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package cmd
    16  
    17  import (
    18  	"context"
    19  	"encoding/json"
    20  	"os"
    21  	"time"
    22  
    23  	"github.com/google/subcommands"
    24  	"gvisor.dev/gvisor/pkg/log"
    25  	"gvisor.dev/gvisor/runsc/cmd/util"
    26  	"gvisor.dev/gvisor/runsc/config"
    27  	"gvisor.dev/gvisor/runsc/container"
    28  	"gvisor.dev/gvisor/runsc/flag"
    29  )
    30  
    31  // Events implements subcommands.Command for the "events" command.
    32  type Events struct {
    33  	// The interval between stats reporting.
    34  	intervalSec int
    35  	// If true, events will print a single group of stats and exit.
    36  	stats bool
    37  }
    38  
    39  // Name implements subcommands.Command.Name.
    40  func (*Events) Name() string {
    41  	return "events"
    42  }
    43  
    44  // Synopsis implements subcommands.Command.Synopsis.
    45  func (*Events) Synopsis() string {
    46  	return "display container events such as OOM notifications, cpu, memory, and IO usage statistics"
    47  }
    48  
    49  // Usage implements subcommands.Command.Usage.
    50  func (*Events) Usage() string {
    51  	return `<container-id>
    52  
    53  Where "<container-id>" is the name for the instance of the container.
    54  
    55  The events command displays information about the container. By default the
    56  information is displayed once every 5 seconds.
    57  
    58  OPTIONS:
    59  `
    60  }
    61  
    62  // SetFlags implements subcommands.Command.SetFlags.
    63  func (evs *Events) SetFlags(f *flag.FlagSet) {
    64  	f.IntVar(&evs.intervalSec, "interval", 5, "set the stats collection interval, in seconds")
    65  	f.BoolVar(&evs.stats, "stats", false, "display the container's stats then exit")
    66  }
    67  
    68  // Execute implements subcommands.Command.Execute.
    69  func (evs *Events) Execute(_ context.Context, f *flag.FlagSet, args ...any) subcommands.ExitStatus {
    70  	if f.NArg() != 1 {
    71  		f.Usage()
    72  		return subcommands.ExitUsageError
    73  	}
    74  
    75  	id := f.Arg(0)
    76  	conf := args[0].(*config.Config)
    77  
    78  	c, err := container.Load(conf.RootDir, container.FullID{ContainerID: id}, container.LoadOpts{})
    79  	if err != nil {
    80  		util.Fatalf("loading sandbox: %v", err)
    81  	}
    82  
    83  	// Repeatedly get stats from the container. Sleep a bit after every loop
    84  	// except the first one.
    85  	for dur := time.Duration(evs.intervalSec) * time.Second; true; time.Sleep(dur) {
    86  		// Get the event and print it as JSON.
    87  		ev, err := c.Event()
    88  		if err != nil {
    89  			log.Warningf("Error getting events for container: %v", err)
    90  			if evs.stats {
    91  				return subcommands.ExitFailure
    92  			}
    93  			continue
    94  		}
    95  		log.Debugf("Events: %+v", ev)
    96  
    97  		if err := json.NewEncoder(os.Stdout).Encode(ev.Event); err != nil {
    98  			log.Warningf("Error encoding event %+v: %v", ev.Event, err)
    99  			if evs.stats {
   100  				return subcommands.ExitFailure
   101  			}
   102  			continue
   103  		}
   104  
   105  		// Break if we're only running once. If we got this far it was a success.
   106  		if evs.stats {
   107  			return subcommands.ExitSuccess
   108  		}
   109  	}
   110  	panic("should never get here")
   111  }