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