github.com/rish1988/moby@v25.0.2+incompatible/api/server/router/system/system_routes.go (about)

     1  package system // import "github.com/docker/docker/api/server/router/system"
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"fmt"
     7  	"net/http"
     8  	"time"
     9  
    10  	"github.com/containerd/log"
    11  	"github.com/docker/docker/api/server/httputils"
    12  	"github.com/docker/docker/api/server/router/build"
    13  	"github.com/docker/docker/api/types"
    14  	"github.com/docker/docker/api/types/events"
    15  	"github.com/docker/docker/api/types/filters"
    16  	"github.com/docker/docker/api/types/registry"
    17  	"github.com/docker/docker/api/types/swarm"
    18  	"github.com/docker/docker/api/types/system"
    19  	timetypes "github.com/docker/docker/api/types/time"
    20  	"github.com/docker/docker/api/types/versions"
    21  	"github.com/docker/docker/pkg/ioutils"
    22  	"github.com/pkg/errors"
    23  	"golang.org/x/sync/errgroup"
    24  )
    25  
    26  func optionsHandler(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
    27  	w.WriteHeader(http.StatusOK)
    28  	return nil
    29  }
    30  
    31  func (s *systemRouter) pingHandler(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
    32  	w.Header().Add("Cache-Control", "no-cache, no-store, must-revalidate")
    33  	w.Header().Add("Pragma", "no-cache")
    34  
    35  	builderVersion := build.BuilderVersion(s.features())
    36  	if bv := builderVersion; bv != "" {
    37  		w.Header().Set("Builder-Version", string(bv))
    38  	}
    39  
    40  	w.Header().Set("Swarm", s.swarmStatus())
    41  
    42  	if r.Method == http.MethodHead {
    43  		w.Header().Set("Content-Type", "text/plain; charset=utf-8")
    44  		w.Header().Set("Content-Length", "0")
    45  		return nil
    46  	}
    47  	_, err := w.Write([]byte{'O', 'K'})
    48  	return err
    49  }
    50  
    51  func (s *systemRouter) swarmStatus() string {
    52  	if s.cluster != nil {
    53  		if p, ok := s.cluster.(StatusProvider); ok {
    54  			return p.Status()
    55  		}
    56  	}
    57  	return string(swarm.LocalNodeStateInactive)
    58  }
    59  
    60  func (s *systemRouter) getInfo(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
    61  	version := httputils.VersionFromContext(ctx)
    62  	info, _, _ := s.collectSystemInfo.Do(ctx, version, func(ctx context.Context) (*system.Info, error) {
    63  		info, err := s.backend.SystemInfo(ctx)
    64  		if err != nil {
    65  			return nil, err
    66  		}
    67  
    68  		if s.cluster != nil {
    69  			info.Swarm = s.cluster.Info(ctx)
    70  			info.Warnings = append(info.Warnings, info.Swarm.Warnings...)
    71  		}
    72  
    73  		if versions.LessThan(version, "1.25") {
    74  			// TODO: handle this conversion in engine-api
    75  			kvSecOpts, err := system.DecodeSecurityOptions(info.SecurityOptions)
    76  			if err != nil {
    77  				info.Warnings = append(info.Warnings, err.Error())
    78  			}
    79  			var nameOnly []string
    80  			for _, so := range kvSecOpts {
    81  				nameOnly = append(nameOnly, so.Name)
    82  			}
    83  			info.SecurityOptions = nameOnly
    84  			info.ExecutionDriver = "<not supported>" //nolint:staticcheck // ignore SA1019 (ExecutionDriver is deprecated)
    85  		}
    86  		if versions.LessThan(version, "1.39") {
    87  			if info.KernelVersion == "" {
    88  				info.KernelVersion = "<unknown>"
    89  			}
    90  			if info.OperatingSystem == "" {
    91  				info.OperatingSystem = "<unknown>"
    92  			}
    93  		}
    94  		if versions.LessThan(version, "1.44") {
    95  			for k, rt := range info.Runtimes {
    96  				// Status field introduced in API v1.44.
    97  				info.Runtimes[k] = system.RuntimeWithStatus{Runtime: rt.Runtime}
    98  			}
    99  		}
   100  		if versions.GreaterThanOrEqualTo(version, "1.42") {
   101  			info.KernelMemory = false
   102  		}
   103  		return info, nil
   104  	})
   105  	return httputils.WriteJSON(w, http.StatusOK, info)
   106  }
   107  
   108  func (s *systemRouter) getVersion(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   109  	info, err := s.backend.SystemVersion(ctx)
   110  	if err != nil {
   111  		return err
   112  	}
   113  
   114  	return httputils.WriteJSON(w, http.StatusOK, info)
   115  }
   116  
   117  func (s *systemRouter) getDiskUsage(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   118  	if err := httputils.ParseForm(r); err != nil {
   119  		return err
   120  	}
   121  
   122  	version := httputils.VersionFromContext(ctx)
   123  
   124  	var getContainers, getImages, getVolumes, getBuildCache bool
   125  	typeStrs, ok := r.Form["type"]
   126  	if versions.LessThan(version, "1.42") || !ok {
   127  		getContainers, getImages, getVolumes, getBuildCache = true, true, true, s.builder != nil
   128  	} else {
   129  		for _, typ := range typeStrs {
   130  			switch types.DiskUsageObject(typ) {
   131  			case types.ContainerObject:
   132  				getContainers = true
   133  			case types.ImageObject:
   134  				getImages = true
   135  			case types.VolumeObject:
   136  				getVolumes = true
   137  			case types.BuildCacheObject:
   138  				getBuildCache = true
   139  			default:
   140  				return invalidRequestError{Err: fmt.Errorf("unknown object type: %s", typ)}
   141  			}
   142  		}
   143  	}
   144  
   145  	eg, ctx := errgroup.WithContext(ctx)
   146  
   147  	var systemDiskUsage *types.DiskUsage
   148  	if getContainers || getImages || getVolumes {
   149  		eg.Go(func() error {
   150  			var err error
   151  			systemDiskUsage, err = s.backend.SystemDiskUsage(ctx, DiskUsageOptions{
   152  				Containers: getContainers,
   153  				Images:     getImages,
   154  				Volumes:    getVolumes,
   155  			})
   156  			return err
   157  		})
   158  	}
   159  
   160  	var buildCache []*types.BuildCache
   161  	if getBuildCache {
   162  		eg.Go(func() error {
   163  			var err error
   164  			buildCache, err = s.builder.DiskUsage(ctx)
   165  			if err != nil {
   166  				return errors.Wrap(err, "error getting build cache usage")
   167  			}
   168  			if buildCache == nil {
   169  				// Ensure empty `BuildCache` field is represented as empty JSON array(`[]`)
   170  				// instead of `null` to be consistent with `Images`, `Containers` etc.
   171  				buildCache = []*types.BuildCache{}
   172  			}
   173  			return nil
   174  		})
   175  	}
   176  
   177  	if err := eg.Wait(); err != nil {
   178  		return err
   179  	}
   180  
   181  	var builderSize int64
   182  	if versions.LessThan(version, "1.42") {
   183  		for _, b := range buildCache {
   184  			builderSize += b.Size
   185  			// Parents field was added in API 1.42 to replace the Parent field.
   186  			b.Parents = nil
   187  		}
   188  	}
   189  	if versions.GreaterThanOrEqualTo(version, "1.42") {
   190  		for _, b := range buildCache {
   191  			// Parent field is deprecated in API v1.42 and up, as it is deprecated
   192  			// in BuildKit. Empty the field to omit it in the API response.
   193  			b.Parent = "" //nolint:staticcheck // ignore SA1019 (Parent field is deprecated)
   194  		}
   195  	}
   196  	if versions.LessThan(version, "1.44") {
   197  		for _, b := range systemDiskUsage.Images {
   198  			b.VirtualSize = b.Size //nolint:staticcheck // ignore SA1019: field is deprecated, but still set on API < v1.44.
   199  		}
   200  	}
   201  
   202  	du := types.DiskUsage{
   203  		BuildCache:  buildCache,
   204  		BuilderSize: builderSize,
   205  	}
   206  	if systemDiskUsage != nil {
   207  		du.LayersSize = systemDiskUsage.LayersSize
   208  		du.Images = systemDiskUsage.Images
   209  		du.Containers = systemDiskUsage.Containers
   210  		du.Volumes = systemDiskUsage.Volumes
   211  	}
   212  	return httputils.WriteJSON(w, http.StatusOK, du)
   213  }
   214  
   215  type invalidRequestError struct {
   216  	Err error
   217  }
   218  
   219  func (e invalidRequestError) Error() string {
   220  	return e.Err.Error()
   221  }
   222  
   223  func (e invalidRequestError) InvalidParameter() {}
   224  
   225  func (s *systemRouter) getEvents(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   226  	if err := httputils.ParseForm(r); err != nil {
   227  		return err
   228  	}
   229  
   230  	since, err := eventTime(r.Form.Get("since"))
   231  	if err != nil {
   232  		return err
   233  	}
   234  	until, err := eventTime(r.Form.Get("until"))
   235  	if err != nil {
   236  		return err
   237  	}
   238  
   239  	var (
   240  		timeout        <-chan time.Time
   241  		onlyPastEvents bool
   242  	)
   243  	if !until.IsZero() {
   244  		if until.Before(since) {
   245  			return invalidRequestError{fmt.Errorf("`since` time (%s) cannot be after `until` time (%s)", r.Form.Get("since"), r.Form.Get("until"))}
   246  		}
   247  
   248  		now := time.Now()
   249  
   250  		onlyPastEvents = until.Before(now)
   251  
   252  		if !onlyPastEvents {
   253  			dur := until.Sub(now)
   254  			timer := time.NewTimer(dur)
   255  			defer timer.Stop()
   256  			timeout = timer.C
   257  		}
   258  	}
   259  
   260  	ef, err := filters.FromJSON(r.Form.Get("filters"))
   261  	if err != nil {
   262  		return err
   263  	}
   264  
   265  	w.Header().Set("Content-Type", "application/json")
   266  	output := ioutils.NewWriteFlusher(w)
   267  	defer output.Close()
   268  	output.Flush()
   269  
   270  	enc := json.NewEncoder(output)
   271  
   272  	buffered, l := s.backend.SubscribeToEvents(since, until, ef)
   273  	defer s.backend.UnsubscribeFromEvents(l)
   274  
   275  	for _, ev := range buffered {
   276  		if err := enc.Encode(ev); err != nil {
   277  			return err
   278  		}
   279  	}
   280  
   281  	if onlyPastEvents {
   282  		return nil
   283  	}
   284  
   285  	for {
   286  		select {
   287  		case ev := <-l:
   288  			jev, ok := ev.(events.Message)
   289  			if !ok {
   290  				log.G(ctx).Warnf("unexpected event message: %q", ev)
   291  				continue
   292  			}
   293  			if err := enc.Encode(jev); err != nil {
   294  				return err
   295  			}
   296  		case <-timeout:
   297  			return nil
   298  		case <-ctx.Done():
   299  			log.G(ctx).Debug("Client context cancelled, stop sending events")
   300  			return nil
   301  		}
   302  	}
   303  }
   304  
   305  func (s *systemRouter) postAuth(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   306  	var config *registry.AuthConfig
   307  	err := json.NewDecoder(r.Body).Decode(&config)
   308  	r.Body.Close()
   309  	if err != nil {
   310  		return err
   311  	}
   312  	status, token, err := s.backend.AuthenticateToRegistry(ctx, config)
   313  	if err != nil {
   314  		return err
   315  	}
   316  	return httputils.WriteJSON(w, http.StatusOK, &registry.AuthenticateOKBody{
   317  		Status:        status,
   318  		IdentityToken: token,
   319  	})
   320  }
   321  
   322  func eventTime(formTime string) (time.Time, error) {
   323  	t, tNano, err := timetypes.ParseTimestamps(formTime, -1)
   324  	if err != nil {
   325  		return time.Time{}, err
   326  	}
   327  	if t == -1 {
   328  		return time.Time{}, nil
   329  	}
   330  	return time.Unix(t, tNano), nil
   331  }