123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331 |
- package system // import "github.com/docker/docker/api/server/router/system"
- import (
- "context"
- "encoding/json"
- "fmt"
- "net/http"
- "time"
- "github.com/containerd/log"
- "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"
- "github.com/docker/docker/api/types/system"
- 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"
- "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 {
- version := httputils.VersionFromContext(ctx)
- info, _, _ := s.collectSystemInfo.Do(ctx, version, func(ctx context.Context) (*system.Info, error) {
- info, err := s.backend.SystemInfo(ctx)
- if err != nil {
- return nil, err
- }
- if s.cluster != nil {
- info.Swarm = s.cluster.Info(ctx)
- info.Warnings = append(info.Warnings, info.Swarm.Warnings...)
- }
- if versions.LessThan(version, "1.25") {
- // TODO: handle this conversion in engine-api
- kvSecOpts, err := system.DecodeSecurityOptions(info.SecurityOptions)
- if err != nil {
- info.Warnings = append(info.Warnings, err.Error())
- }
- var nameOnly []string
- for _, so := range kvSecOpts {
- nameOnly = append(nameOnly, so.Name)
- }
- info.SecurityOptions = nameOnly
- info.ExecutionDriver = "<not supported>" //nolint:staticcheck // ignore SA1019 (ExecutionDriver is deprecated)
- }
- if versions.LessThan(version, "1.39") {
- if info.KernelVersion == "" {
- info.KernelVersion = "<unknown>"
- }
- if info.OperatingSystem == "" {
- info.OperatingSystem = "<unknown>"
- }
- }
- if versions.LessThan(version, "1.44") {
- for k, rt := range info.Runtimes {
- // Status field introduced in API v1.44.
- info.Runtimes[k] = system.RuntimeWithStatus{Runtime: rt.Runtime}
- }
- }
- if versions.GreaterThanOrEqualTo(version, "1.42") {
- info.KernelMemory = false
- }
- return info, nil
- })
- 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, err := s.backend.SystemVersion(ctx)
- if err != nil {
- return err
- }
- 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, s.builder != nil
- } 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)
- }
- }
- if versions.LessThan(version, "1.44") {
- for _, b := range systemDiskUsage.Images {
- b.VirtualSize = b.Size //nolint:staticcheck // ignore SA1019: field is deprecated, but still set on API < v1.44.
- }
- }
- 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 {
- log.G(ctx).Warnf("unexpected event message: %q", ev)
- continue
- }
- if err := enc.Encode(jev); err != nil {
- return err
- }
- case <-timeout:
- return nil
- case <-ctx.Done():
- log.G(ctx).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
- }
|