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