mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
e0db8207f3
This field was added to replace the deprecated "Parent" field. Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
318 lines
8.1 KiB
Go
318 lines
8.1 KiB
Go
package system // import "github.com/docker/docker/api/server/router/system"
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/docker/docker/api/server/httputils"
|
|
"github.com/docker/docker/api/server/router/build"
|
|
"github.com/docker/docker/api/types"
|
|
"github.com/docker/docker/api/types/events"
|
|
"github.com/docker/docker/api/types/filters"
|
|
"github.com/docker/docker/api/types/registry"
|
|
"github.com/docker/docker/api/types/swarm"
|
|
timetypes "github.com/docker/docker/api/types/time"
|
|
"github.com/docker/docker/api/types/versions"
|
|
"github.com/docker/docker/pkg/ioutils"
|
|
"github.com/pkg/errors"
|
|
"github.com/sirupsen/logrus"
|
|
"golang.org/x/sync/errgroup"
|
|
)
|
|
|
|
func optionsHandler(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
w.WriteHeader(http.StatusOK)
|
|
return nil
|
|
}
|
|
|
|
func (s *systemRouter) pingHandler(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
w.Header().Add("Cache-Control", "no-cache, no-store, must-revalidate")
|
|
w.Header().Add("Pragma", "no-cache")
|
|
|
|
builderVersion := build.BuilderVersion(*s.features)
|
|
if bv := builderVersion; bv != "" {
|
|
w.Header().Set("Builder-Version", string(bv))
|
|
}
|
|
|
|
w.Header().Set("Swarm", s.swarmStatus())
|
|
|
|
if r.Method == http.MethodHead {
|
|
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
|
w.Header().Set("Content-Length", "0")
|
|
return nil
|
|
}
|
|
_, err := w.Write([]byte{'O', 'K'})
|
|
return err
|
|
}
|
|
|
|
func (s *systemRouter) swarmStatus() string {
|
|
if s.cluster != nil {
|
|
if p, ok := s.cluster.(StatusProvider); ok {
|
|
return p.Status()
|
|
}
|
|
}
|
|
return string(swarm.LocalNodeStateInactive)
|
|
}
|
|
|
|
func (s *systemRouter) getInfo(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
info := s.backend.SystemInfo()
|
|
|
|
if s.cluster != nil {
|
|
info.Swarm = s.cluster.Info()
|
|
info.Warnings = append(info.Warnings, info.Swarm.Warnings...)
|
|
}
|
|
|
|
version := httputils.VersionFromContext(ctx)
|
|
if versions.LessThan(version, "1.25") {
|
|
// TODO: handle this conversion in engine-api
|
|
type oldInfo struct {
|
|
*types.Info
|
|
ExecutionDriver string
|
|
}
|
|
old := &oldInfo{
|
|
Info: info,
|
|
ExecutionDriver: "<not supported>",
|
|
}
|
|
nameOnlySecurityOptions := []string{}
|
|
kvSecOpts, err := types.DecodeSecurityOptions(old.SecurityOptions)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, s := range kvSecOpts {
|
|
nameOnlySecurityOptions = append(nameOnlySecurityOptions, s.Name)
|
|
}
|
|
old.SecurityOptions = nameOnlySecurityOptions
|
|
return httputils.WriteJSON(w, http.StatusOK, old)
|
|
}
|
|
if versions.LessThan(version, "1.39") {
|
|
if info.KernelVersion == "" {
|
|
info.KernelVersion = "<unknown>"
|
|
}
|
|
if info.OperatingSystem == "" {
|
|
info.OperatingSystem = "<unknown>"
|
|
}
|
|
}
|
|
if versions.GreaterThanOrEqualTo(version, "1.42") {
|
|
info.KernelMemory = false
|
|
}
|
|
return httputils.WriteJSON(w, http.StatusOK, info)
|
|
}
|
|
|
|
func (s *systemRouter) getVersion(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
info := s.backend.SystemVersion()
|
|
|
|
return httputils.WriteJSON(w, http.StatusOK, info)
|
|
}
|
|
|
|
func (s *systemRouter) getDiskUsage(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
if err := httputils.ParseForm(r); err != nil {
|
|
return err
|
|
}
|
|
|
|
version := httputils.VersionFromContext(ctx)
|
|
|
|
var getContainers, getImages, getVolumes, getBuildCache bool
|
|
typeStrs, ok := r.Form["type"]
|
|
if versions.LessThan(version, "1.42") || !ok {
|
|
getContainers, getImages, getVolumes, getBuildCache = true, true, true, true
|
|
} else {
|
|
for _, typ := range typeStrs {
|
|
switch types.DiskUsageObject(typ) {
|
|
case types.ContainerObject:
|
|
getContainers = true
|
|
case types.ImageObject:
|
|
getImages = true
|
|
case types.VolumeObject:
|
|
getVolumes = true
|
|
case types.BuildCacheObject:
|
|
getBuildCache = true
|
|
default:
|
|
return invalidRequestError{Err: fmt.Errorf("unknown object type: %s", typ)}
|
|
}
|
|
}
|
|
}
|
|
|
|
eg, ctx := errgroup.WithContext(ctx)
|
|
|
|
var systemDiskUsage *types.DiskUsage
|
|
if getContainers || getImages || getVolumes {
|
|
eg.Go(func() error {
|
|
var err error
|
|
systemDiskUsage, err = s.backend.SystemDiskUsage(ctx, DiskUsageOptions{
|
|
Containers: getContainers,
|
|
Images: getImages,
|
|
Volumes: getVolumes,
|
|
})
|
|
return err
|
|
})
|
|
}
|
|
|
|
var buildCache []*types.BuildCache
|
|
if getBuildCache {
|
|
eg.Go(func() error {
|
|
var err error
|
|
buildCache, err = s.builder.DiskUsage(ctx)
|
|
if err != nil {
|
|
return errors.Wrap(err, "error getting build cache usage")
|
|
}
|
|
if buildCache == nil {
|
|
// Ensure empty `BuildCache` field is represented as empty JSON array(`[]`)
|
|
// instead of `null` to be consistent with `Images`, `Containers` etc.
|
|
buildCache = []*types.BuildCache{}
|
|
}
|
|
return nil
|
|
})
|
|
}
|
|
|
|
if err := eg.Wait(); err != nil {
|
|
return err
|
|
}
|
|
|
|
var builderSize int64
|
|
if versions.LessThan(version, "1.42") {
|
|
for _, b := range buildCache {
|
|
builderSize += b.Size
|
|
// Parents field was added in API 1.42 to replace the Parent field.
|
|
b.Parents = nil
|
|
}
|
|
}
|
|
if versions.GreaterThanOrEqualTo(version, "1.42") {
|
|
for _, b := range buildCache {
|
|
// Parent field is deprecated in API v1.42 and up, as it is deprecated
|
|
// in BuildKit. Empty the field to omit it in the API response.
|
|
b.Parent = "" //nolint:staticcheck // ignore SA1019 (Parent field is deprecated)
|
|
}
|
|
}
|
|
|
|
du := types.DiskUsage{
|
|
BuildCache: buildCache,
|
|
BuilderSize: builderSize,
|
|
}
|
|
if systemDiskUsage != nil {
|
|
du.LayersSize = systemDiskUsage.LayersSize
|
|
du.Images = systemDiskUsage.Images
|
|
du.Containers = systemDiskUsage.Containers
|
|
du.Volumes = systemDiskUsage.Volumes
|
|
}
|
|
return httputils.WriteJSON(w, http.StatusOK, du)
|
|
}
|
|
|
|
type invalidRequestError struct {
|
|
Err error
|
|
}
|
|
|
|
func (e invalidRequestError) Error() string {
|
|
return e.Err.Error()
|
|
}
|
|
|
|
func (e invalidRequestError) InvalidParameter() {}
|
|
|
|
func (s *systemRouter) getEvents(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
if err := httputils.ParseForm(r); err != nil {
|
|
return err
|
|
}
|
|
|
|
since, err := eventTime(r.Form.Get("since"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
until, err := eventTime(r.Form.Get("until"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var (
|
|
timeout <-chan time.Time
|
|
onlyPastEvents bool
|
|
)
|
|
if !until.IsZero() {
|
|
if until.Before(since) {
|
|
return invalidRequestError{fmt.Errorf("`since` time (%s) cannot be after `until` time (%s)", r.Form.Get("since"), r.Form.Get("until"))}
|
|
}
|
|
|
|
now := time.Now()
|
|
|
|
onlyPastEvents = until.Before(now)
|
|
|
|
if !onlyPastEvents {
|
|
dur := until.Sub(now)
|
|
timer := time.NewTimer(dur)
|
|
defer timer.Stop()
|
|
timeout = timer.C
|
|
}
|
|
}
|
|
|
|
ef, err := filters.FromJSON(r.Form.Get("filters"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
output := ioutils.NewWriteFlusher(w)
|
|
defer output.Close()
|
|
output.Flush()
|
|
|
|
enc := json.NewEncoder(output)
|
|
|
|
buffered, l := s.backend.SubscribeToEvents(since, until, ef)
|
|
defer s.backend.UnsubscribeFromEvents(l)
|
|
|
|
for _, ev := range buffered {
|
|
if err := enc.Encode(ev); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if onlyPastEvents {
|
|
return nil
|
|
}
|
|
|
|
for {
|
|
select {
|
|
case ev := <-l:
|
|
jev, ok := ev.(events.Message)
|
|
if !ok {
|
|
logrus.Warnf("unexpected event message: %q", ev)
|
|
continue
|
|
}
|
|
if err := enc.Encode(jev); err != nil {
|
|
return err
|
|
}
|
|
case <-timeout:
|
|
return nil
|
|
case <-ctx.Done():
|
|
logrus.Debug("Client context cancelled, stop sending events")
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
|
|
func (s *systemRouter) postAuth(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
var config *registry.AuthConfig
|
|
err := json.NewDecoder(r.Body).Decode(&config)
|
|
r.Body.Close()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
status, token, err := s.backend.AuthenticateToRegistry(ctx, config)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return httputils.WriteJSON(w, http.StatusOK, ®istry.AuthenticateOKBody{
|
|
Status: status,
|
|
IdentityToken: token,
|
|
})
|
|
}
|
|
|
|
func eventTime(formTime string) (time.Time, error) {
|
|
t, tNano, err := timetypes.ParseTimestamps(formTime, -1)
|
|
if err != nil {
|
|
return time.Time{}, err
|
|
}
|
|
if t == -1 {
|
|
return time.Time{}, nil
|
|
}
|
|
return time.Unix(t, tNano), nil
|
|
}
|