github.com/hanks177/podman/v4@v4.1.3-0.20220613032544-16d90015bc83/pkg/api/handlers/compat/events.go (about)

     1  package compat
     2  
     3  import (
     4  	"net/http"
     5  
     6  	"github.com/hanks177/podman/v4/libpod"
     7  	"github.com/hanks177/podman/v4/libpod/events"
     8  	"github.com/hanks177/podman/v4/pkg/api/handlers/utils"
     9  	api "github.com/hanks177/podman/v4/pkg/api/types"
    10  	"github.com/hanks177/podman/v4/pkg/domain/entities"
    11  	"github.com/hanks177/podman/v4/pkg/util"
    12  	"github.com/gorilla/schema"
    13  	jsoniter "github.com/json-iterator/go"
    14  	"github.com/pkg/errors"
    15  	"github.com/sirupsen/logrus"
    16  )
    17  
    18  // GetEvents endpoint serves both the docker-compatible one and the new libpod one
    19  func GetEvents(w http.ResponseWriter, r *http.Request) {
    20  	var (
    21  		fromStart bool
    22  		decoder   = r.Context().Value(api.DecoderKey).(*schema.Decoder)
    23  		runtime   = r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
    24  		json      = jsoniter.ConfigCompatibleWithStandardLibrary // FIXME: this should happen on the package level
    25  	)
    26  
    27  	// NOTE: the "filters" parameter is extracted separately for backwards
    28  	// compat via `filterFromRequest()`.
    29  	query := struct {
    30  		Since  string `schema:"since"`
    31  		Until  string `schema:"until"`
    32  		Stream bool   `schema:"stream"`
    33  	}{
    34  		Stream: true,
    35  	}
    36  	if err := decoder.Decode(&query, r.URL.Query()); err != nil {
    37  		utils.Error(w, http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
    38  		return
    39  	}
    40  
    41  	if len(query.Since) > 0 || len(query.Until) > 0 {
    42  		fromStart = true
    43  	}
    44  
    45  	libpodFilters, err := util.FiltersFromRequest(r)
    46  	if err != nil {
    47  		utils.Error(w, http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
    48  		return
    49  	}
    50  	eventChannel := make(chan *events.Event)
    51  	errorChannel := make(chan error)
    52  
    53  	// Start reading events.
    54  	go func() {
    55  		readOpts := events.ReadOptions{
    56  			FromStart:    fromStart,
    57  			Stream:       query.Stream,
    58  			Filters:      libpodFilters,
    59  			EventChannel: eventChannel,
    60  			Since:        query.Since,
    61  			Until:        query.Until,
    62  		}
    63  		errorChannel <- runtime.Events(r.Context(), readOpts)
    64  	}()
    65  
    66  	flush := func() {}
    67  	if flusher, ok := w.(http.Flusher); ok {
    68  		flush = flusher.Flush
    69  	}
    70  
    71  	w.Header().Set("Content-Type", "application/json")
    72  	w.WriteHeader(http.StatusOK)
    73  	flush()
    74  
    75  	coder := json.NewEncoder(w)
    76  	coder.SetEscapeHTML(true)
    77  
    78  	for {
    79  		select {
    80  		case err := <-errorChannel:
    81  			if err != nil {
    82  				// FIXME StatusOK already sent above cannot send 500 here
    83  				utils.InternalServerError(w, err)
    84  			}
    85  			return
    86  		case evt := <-eventChannel:
    87  			if evt == nil {
    88  				continue
    89  			}
    90  
    91  			e := entities.ConvertToEntitiesEvent(*evt)
    92  			if !utils.IsLibpodRequest(r) && e.Status == "died" {
    93  				e.Status = "die"
    94  				e.Action = "die"
    95  				e.Actor.Attributes["exitCode"] = e.Actor.Attributes["containerExitCode"]
    96  			}
    97  
    98  			if err := coder.Encode(e); err != nil {
    99  				logrus.Errorf("Unable to write json: %q", err)
   100  			}
   101  			flush()
   102  		case <-r.Context().Done():
   103  			return
   104  		}
   105  	}
   106  }