mirror of
https://github.com/containers/podman.git
synced 2025-08-06 03:19:52 +08:00
Merge pull request #6620 from jgallucci32/api-logs-separate
Move logs functionality to separate file for APIv2
This commit is contained in:
@ -1,29 +1,21 @@
|
||||
package compat
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/containers/libpod/libpod"
|
||||
"github.com/containers/libpod/libpod/define"
|
||||
"github.com/containers/libpod/libpod/logs"
|
||||
"github.com/containers/libpod/pkg/api/handlers"
|
||||
"github.com/containers/libpod/pkg/api/handlers/utils"
|
||||
"github.com/containers/libpod/pkg/signal"
|
||||
"github.com/containers/libpod/pkg/util"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/gorilla/schema"
|
||||
"github.com/pkg/errors"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func RemoveContainer(w http.ResponseWriter, r *http.Request) {
|
||||
@ -213,140 +205,6 @@ func WaitContainer(w http.ResponseWriter, r *http.Request) {
|
||||
})
|
||||
}
|
||||
|
||||
func LogsFromContainer(w http.ResponseWriter, r *http.Request) {
|
||||
decoder := r.Context().Value("decoder").(*schema.Decoder)
|
||||
runtime := r.Context().Value("runtime").(*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, "Something went wrong.", 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, msg, 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)
|
||||
if err != nil {
|
||||
utils.BadRequest(w, "since", query.Since, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var until time.Time
|
||||
if _, found := r.URL.Query()["until"]; found {
|
||||
// FIXME: until != since but the logs backend does not yet support until.
|
||||
since, err = util.ParseInputTime(query.Until)
|
||||
if err != nil {
|
||||
utils.BadRequest(w, "until", query.Until, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
options := &logs.LogOptions{
|
||||
Details: true,
|
||||
Follow: query.Follow,
|
||||
Since: since,
|
||||
Tail: tail,
|
||||
Timestamps: query.Timestamps,
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
options.WaitGroup = &wg
|
||||
|
||||
logChannel := make(chan *logs.LogLine, tail+1)
|
||||
if err := runtime.Log([]*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)
|
||||
for ok := true; ok; ok = query.Follow {
|
||||
for line := range logChannel {
|
||||
if _, found := r.URL.Query()["until"]; found {
|
||||
if line.Time.After(until) {
|
||||
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)
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func LibpodToContainer(l *libpod.Container, sz bool) (*handlers.Container, error) {
|
||||
imageID, imageName := l.Image()
|
||||
|
||||
|
154
pkg/api/handlers/compat/containers_logs.go
Normal file
154
pkg/api/handlers/compat/containers_logs.go
Normal file
@ -0,0 +1,154 @@
|
||||
package compat
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/containers/libpod/libpod"
|
||||
"github.com/containers/libpod/libpod/logs"
|
||||
"github.com/containers/libpod/pkg/api/handlers/utils"
|
||||
"github.com/containers/libpod/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("decoder").(*schema.Decoder)
|
||||
runtime := r.Context().Value("runtime").(*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, "Something went wrong.", 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, msg, 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)
|
||||
if err != nil {
|
||||
utils.BadRequest(w, "since", query.Since, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var until time.Time
|
||||
if _, found := r.URL.Query()["until"]; found {
|
||||
// FIXME: until != since but the logs backend does not yet support until.
|
||||
since, err = util.ParseInputTime(query.Until)
|
||||
if err != nil {
|
||||
utils.BadRequest(w, "until", query.Until, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
options := &logs.LogOptions{
|
||||
Details: true,
|
||||
Follow: query.Follow,
|
||||
Since: since,
|
||||
Tail: tail,
|
||||
Timestamps: query.Timestamps,
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
options.WaitGroup = &wg
|
||||
|
||||
logChannel := make(chan *logs.LogLine, tail+1)
|
||||
if err := runtime.Log([]*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)
|
||||
for ok := true; ok; ok = query.Follow {
|
||||
for line := range logChannel {
|
||||
if _, found := r.URL.Query()["until"]; found {
|
||||
if line.Time.After(until) {
|
||||
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)
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user