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, ®istry.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 }