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