This defines a 'context' object that is passed to each API handler. Right now the context just has a unique 'requestID' for each API call. The next steps would be: - use this 'requestID' in our logging. - determine the best way to format the logging to include this info. In particular for log events that generate multiple entries in the log we can use the requestID to help correlate the log entries. Adding the requestID to the logging will be a challenge since it could mean changing every single logrus.XXX() call to pass in the 'context' object. But first step is to agree on a format, which we can discus in a subsequent PR, but my initial thoughts are to add it right after the timestamp: current format: INFO[0039] POST /v1.21/build?buildargs=%7B%22foo%22%3A%22xxx%22%7D&cgroupparent=&cpuperiod=0&cpuquota=0&cpusetcpus=&cpusetmems=&cpushares=0&dockerfile=Dockerfile&memory=0&memswap=0&rm=1&t=&ulimits=null proposed format: INFO[0039-83dea1222191] POST /v1.21/build?buildargs=%7B%22foo%22%3A%22xxx%22%7D&cgroupparent=&cpuperiod=0&cpuquota=0&cpusetcpus=&cpusetmems=&cpushares=0&dockerfile=Dockerfile&memory=0&memswap=0&rm=1&t=&ulimits=null Signed-off-by: Doug Davis <dug@us.ibm.com> Upstream-commit: 8b454dd79e6a11c3c881f8a755423713c0491287 Component: engine
179 lines
3.9 KiB
Go
179 lines
3.9 KiB
Go
package server
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"runtime"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"golang.org/x/net/context"
|
|
|
|
"github.com/Sirupsen/logrus"
|
|
"github.com/docker/docker/api"
|
|
"github.com/docker/docker/api/types"
|
|
"github.com/docker/docker/autogen/dockerversion"
|
|
"github.com/docker/docker/pkg/ioutils"
|
|
"github.com/docker/docker/pkg/jsonmessage"
|
|
"github.com/docker/docker/pkg/parsers/filters"
|
|
"github.com/docker/docker/pkg/parsers/kernel"
|
|
"github.com/docker/docker/pkg/version"
|
|
"github.com/docker/docker/utils"
|
|
)
|
|
|
|
func (s *Server) getVersion(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
v := &types.Version{
|
|
Version: dockerversion.VERSION,
|
|
APIVersion: api.Version,
|
|
GitCommit: dockerversion.GITCOMMIT,
|
|
GoVersion: runtime.Version(),
|
|
Os: runtime.GOOS,
|
|
Arch: runtime.GOARCH,
|
|
BuildTime: dockerversion.BUILDTIME,
|
|
}
|
|
|
|
version, _ := ctx.Value("api-version").(version.Version)
|
|
|
|
if version.GreaterThanOrEqualTo("1.19") {
|
|
v.Experimental = utils.ExperimentalBuild()
|
|
}
|
|
|
|
if kernelVersion, err := kernel.GetKernelVersion(); err == nil {
|
|
v.KernelVersion = kernelVersion.String()
|
|
}
|
|
|
|
return writeJSON(w, http.StatusOK, v)
|
|
}
|
|
|
|
func (s *Server) getInfo(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
info, err := s.daemon.SystemInfo()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return writeJSON(w, http.StatusOK, info)
|
|
}
|
|
|
|
func (s *Server) getEvents(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
if err := parseForm(r); err != nil {
|
|
return err
|
|
}
|
|
var since int64 = -1
|
|
if r.Form.Get("since") != "" {
|
|
s, err := strconv.ParseInt(r.Form.Get("since"), 10, 64)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
since = s
|
|
}
|
|
|
|
var until int64 = -1
|
|
if r.Form.Get("until") != "" {
|
|
u, err := strconv.ParseInt(r.Form.Get("until"), 10, 64)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
until = u
|
|
}
|
|
|
|
timer := time.NewTimer(0)
|
|
timer.Stop()
|
|
if until > 0 {
|
|
dur := time.Unix(until, 0).Sub(time.Now())
|
|
timer = time.NewTimer(dur)
|
|
}
|
|
|
|
ef, err := filters.FromParam(r.Form.Get("filters"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
isFiltered := func(field string, filter []string) bool {
|
|
if len(field) == 0 {
|
|
return false
|
|
}
|
|
if len(filter) == 0 {
|
|
return false
|
|
}
|
|
for _, v := range filter {
|
|
if v == field {
|
|
return false
|
|
}
|
|
if strings.Contains(field, ":") {
|
|
image := strings.Split(field, ":")
|
|
if image[0] == v {
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
d := s.daemon
|
|
es := d.EventsService
|
|
w.Header().Set("Content-Type", "application/json")
|
|
outStream := ioutils.NewWriteFlusher(w)
|
|
outStream.Write(nil) // make sure response is sent immediately
|
|
enc := json.NewEncoder(outStream)
|
|
|
|
getContainerID := func(cn string) string {
|
|
c, err := d.Get(cn)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
return c.ID
|
|
}
|
|
|
|
sendEvent := func(ev *jsonmessage.JSONMessage) error {
|
|
//incoming container filter can be name,id or partial id, convert and replace as a full container id
|
|
for i, cn := range ef["container"] {
|
|
ef["container"][i] = getContainerID(cn)
|
|
}
|
|
|
|
if isFiltered(ev.Status, ef["event"]) || (isFiltered(ev.ID, ef["image"]) &&
|
|
isFiltered(ev.From, ef["image"])) || isFiltered(ev.ID, ef["container"]) {
|
|
return nil
|
|
}
|
|
|
|
return enc.Encode(ev)
|
|
}
|
|
|
|
current, l := es.Subscribe()
|
|
if since == -1 {
|
|
current = nil
|
|
}
|
|
defer es.Evict(l)
|
|
for _, ev := range current {
|
|
if ev.Time < since {
|
|
continue
|
|
}
|
|
if err := sendEvent(ev); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
var closeNotify <-chan bool
|
|
if closeNotifier, ok := w.(http.CloseNotifier); ok {
|
|
closeNotify = closeNotifier.CloseNotify()
|
|
}
|
|
|
|
for {
|
|
select {
|
|
case ev := <-l:
|
|
jev, ok := ev.(*jsonmessage.JSONMessage)
|
|
if !ok {
|
|
continue
|
|
}
|
|
if err := sendEvent(jev); err != nil {
|
|
return err
|
|
}
|
|
case <-timer.C:
|
|
return nil
|
|
case <-closeNotify:
|
|
logrus.Debug("Client disconnected, stop sending events")
|
|
return nil
|
|
}
|
|
}
|
|
}
|