github.com/Prakhar-Agarwal-byte/moby@v0.0.0-20231027092010-a14e3e8ab87e/api/server/router/system/system_routes.go (about)

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