mirror of
				https://github.com/containers/podman.git
				synced 2025-10-31 18:08:51 +08:00 
			
		
		
		
	 7938f32c53
			
		
	
	7938f32c53
	
	
	
		
			
			This commit removes error message string from utils.Error in pkg/api. Param was not used inside a function for quite a long time [NO NEW TESTS NEEDED] Signed-off-by: Jakub Guzik <jguzik@redhat.com>
		
			
				
	
	
		
			175 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			175 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package compat
 | |
| 
 | |
| import (
 | |
| 	"encoding/binary"
 | |
| 	"fmt"
 | |
| 	"io"
 | |
| 	"net/http"
 | |
| 	"strconv"
 | |
| 	"strings"
 | |
| 	"sync"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/containers/podman/v4/libpod"
 | |
| 	"github.com/containers/podman/v4/libpod/logs"
 | |
| 	"github.com/containers/podman/v4/pkg/api/handlers/utils"
 | |
| 	api "github.com/containers/podman/v4/pkg/api/types"
 | |
| 	"github.com/containers/podman/v4/pkg/util"
 | |
| 	"github.com/gorilla/schema"
 | |
| 	"github.com/pkg/errors"
 | |
| 	log "github.com/sirupsen/logrus"
 | |
| )
 | |
| 
 | |
| func LogsFromContainer(w http.ResponseWriter, r *http.Request) {
 | |
| 	decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder)
 | |
| 	runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
 | |
| 
 | |
| 	query := struct {
 | |
| 		Follow     bool   `schema:"follow"`
 | |
| 		Stdout     bool   `schema:"stdout"`
 | |
| 		Stderr     bool   `schema:"stderr"`
 | |
| 		Since      string `schema:"since"`
 | |
| 		Until      string `schema:"until"`
 | |
| 		Timestamps bool   `schema:"timestamps"`
 | |
| 		Tail       string `schema:"tail"`
 | |
| 	}{
 | |
| 		Tail: "all",
 | |
| 	}
 | |
| 	if err := decoder.Decode(&query, r.URL.Query()); err != nil {
 | |
| 		utils.Error(w, http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	if !(query.Stdout || query.Stderr) {
 | |
| 		msg := fmt.Sprintf("%s: you must choose at least one stream", http.StatusText(http.StatusBadRequest))
 | |
| 		utils.Error(w, http.StatusBadRequest, errors.Errorf("%s for %s", msg, r.URL.String()))
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	name := utils.GetName(r)
 | |
| 	ctnr, err := runtime.LookupContainer(name)
 | |
| 	if err != nil {
 | |
| 		utils.ContainerNotFound(w, name, err)
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	var tail int64 = -1
 | |
| 	if query.Tail != "all" {
 | |
| 		tail, err = strconv.ParseInt(query.Tail, 0, 64)
 | |
| 		if err != nil {
 | |
| 			utils.BadRequest(w, "tail", query.Tail, err)
 | |
| 			return
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	var since time.Time
 | |
| 	if _, found := r.URL.Query()["since"]; found {
 | |
| 		since, err = util.ParseInputTime(query.Since, true)
 | |
| 		if err != nil {
 | |
| 			utils.BadRequest(w, "since", query.Since, err)
 | |
| 			return
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	var until time.Time
 | |
| 	if _, found := r.URL.Query()["until"]; found {
 | |
| 		if query.Until != "0" {
 | |
| 			until, err = util.ParseInputTime(query.Until, false)
 | |
| 			if err != nil {
 | |
| 				utils.BadRequest(w, "until", query.Until, err)
 | |
| 				return
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	options := &logs.LogOptions{
 | |
| 		Details:    true,
 | |
| 		Follow:     query.Follow,
 | |
| 		Since:      since,
 | |
| 		Until:      until,
 | |
| 		Tail:       tail,
 | |
| 		Timestamps: query.Timestamps,
 | |
| 	}
 | |
| 
 | |
| 	var wg sync.WaitGroup
 | |
| 	options.WaitGroup = &wg
 | |
| 
 | |
| 	logChannel := make(chan *logs.LogLine, tail+1)
 | |
| 	if err := runtime.Log(r.Context(), []*libpod.Container{ctnr}, options, logChannel); err != nil {
 | |
| 		utils.InternalServerError(w, errors.Wrapf(err, "failed to obtain logs for Container '%s'", name))
 | |
| 		return
 | |
| 	}
 | |
| 	go func() {
 | |
| 		wg.Wait()
 | |
| 		close(logChannel)
 | |
| 	}()
 | |
| 
 | |
| 	w.WriteHeader(http.StatusOK)
 | |
| 
 | |
| 	var frame strings.Builder
 | |
| 	header := make([]byte, 8)
 | |
| 
 | |
| 	writeHeader := true
 | |
| 	// Docker does not write stream headers iff the container has a tty.
 | |
| 	if !utils.IsLibpodRequest(r) {
 | |
| 		inspectData, err := ctnr.Inspect(false)
 | |
| 		if err != nil {
 | |
| 			utils.InternalServerError(w, errors.Wrapf(err, "failed to obtain logs for Container '%s'", name))
 | |
| 			return
 | |
| 		}
 | |
| 		writeHeader = !inspectData.Config.Tty
 | |
| 	}
 | |
| 
 | |
| 	for line := range logChannel {
 | |
| 		if _, found := r.URL.Query()["until"]; found {
 | |
| 			if line.Time.After(until) && !until.IsZero() {
 | |
| 				break
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		// Reset buffer we're ready to loop again
 | |
| 		frame.Reset()
 | |
| 		switch line.Device {
 | |
| 		case "stdout":
 | |
| 			if !query.Stdout {
 | |
| 				continue
 | |
| 			}
 | |
| 			header[0] = 1
 | |
| 		case "stderr":
 | |
| 			if !query.Stderr {
 | |
| 				continue
 | |
| 			}
 | |
| 			header[0] = 2
 | |
| 		default:
 | |
| 			// Logging and moving on is the best we can do here. We may have already sent
 | |
| 			// a Status and Content-Type to client therefore we can no longer report an error.
 | |
| 			log.Infof("unknown Device type '%s' in log file from Container %s", line.Device, ctnr.ID())
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		if query.Timestamps {
 | |
| 			frame.WriteString(line.Time.Format(time.RFC3339))
 | |
| 			frame.WriteString(" ")
 | |
| 		}
 | |
| 
 | |
| 		frame.WriteString(line.Msg)
 | |
| 		if !line.Partial() {
 | |
| 			frame.WriteString("\n")
 | |
| 		}
 | |
| 
 | |
| 		if writeHeader {
 | |
| 			binary.BigEndian.PutUint32(header[4:], uint32(frame.Len()))
 | |
| 			if _, err := w.Write(header[0:8]); err != nil {
 | |
| 				log.Errorf("unable to write log output header: %q", err)
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if _, err := io.WriteString(w, frame.String()); err != nil {
 | |
| 			log.Errorf("unable to write frame string: %q", err)
 | |
| 		}
 | |
| 		if flusher, ok := w.(http.Flusher); ok {
 | |
| 			flusher.Flush()
 | |
| 		}
 | |
| 	}
 | |
| }
 |