github.com/toplink-cn/moby@v0.0.0-20240305205811-460b4aebdf81/daemon/info.go (about) 1 // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: 2 //go:build go1.19 3 4 package daemon // import "github.com/docker/docker/daemon" 5 6 import ( 7 "context" 8 "fmt" 9 "os" 10 "runtime" 11 "strings" 12 "time" 13 14 "github.com/containerd/containerd/tracing" 15 "github.com/containerd/log" 16 "github.com/docker/docker/api" 17 "github.com/docker/docker/api/types" 18 "github.com/docker/docker/api/types/system" 19 "github.com/docker/docker/cli/debug" 20 "github.com/docker/docker/daemon/config" 21 "github.com/docker/docker/daemon/logger" 22 "github.com/docker/docker/dockerversion" 23 "github.com/docker/docker/pkg/fileutils" 24 "github.com/docker/docker/pkg/meminfo" 25 "github.com/docker/docker/pkg/parsers/kernel" 26 "github.com/docker/docker/pkg/parsers/operatingsystem" 27 "github.com/docker/docker/pkg/platform" 28 "github.com/docker/docker/pkg/sysinfo" 29 "github.com/docker/docker/registry" 30 metrics "github.com/docker/go-metrics" 31 "github.com/opencontainers/selinux/go-selinux" 32 ) 33 34 func doWithTrace[T any](ctx context.Context, name string, f func() T) T { 35 _, span := tracing.StartSpan(ctx, name) 36 defer span.End() 37 return f() 38 } 39 40 // SystemInfo returns information about the host server the daemon is running on. 41 // 42 // The only error this should return is due to context cancellation/deadline. 43 // Anything else should be logged and ignored because this is looking up 44 // multiple things and is often used for debugging. 45 // The only case valid early return is when the caller doesn't want the result anymore (ie context cancelled). 46 func (daemon *Daemon) SystemInfo(ctx context.Context) (*system.Info, error) { 47 defer metrics.StartTimer(hostInfoFunctions.WithValues("system_info"))() 48 49 sysInfo := daemon.RawSysInfo() 50 cfg := daemon.config() 51 52 v := &system.Info{ 53 ID: daemon.id, 54 Images: daemon.imageService.CountImages(ctx), 55 IPv4Forwarding: !sysInfo.IPv4ForwardingDisabled, 56 BridgeNfIptables: !sysInfo.BridgeNFCallIPTablesDisabled, 57 BridgeNfIP6tables: !sysInfo.BridgeNFCallIP6TablesDisabled, 58 Name: hostName(ctx), 59 SystemTime: time.Now().Format(time.RFC3339Nano), 60 LoggingDriver: daemon.defaultLogConfig.Type, 61 KernelVersion: kernelVersion(ctx), 62 OperatingSystem: operatingSystem(ctx), 63 OSVersion: osVersion(ctx), 64 IndexServerAddress: registry.IndexServer, 65 OSType: runtime.GOOS, 66 Architecture: platform.Architecture, 67 RegistryConfig: doWithTrace(ctx, "registry.ServiceConfig", daemon.registryService.ServiceConfig), 68 NCPU: doWithTrace(ctx, "sysinfo.NumCPU", sysinfo.NumCPU), 69 MemTotal: memInfo(ctx).MemTotal, 70 GenericResources: daemon.genericResources, 71 DockerRootDir: cfg.Root, 72 Labels: cfg.Labels, 73 ExperimentalBuild: cfg.Experimental, 74 ServerVersion: dockerversion.Version, 75 HTTPProxy: config.MaskCredentials(getConfigOrEnv(cfg.HTTPProxy, "HTTP_PROXY", "http_proxy")), 76 HTTPSProxy: config.MaskCredentials(getConfigOrEnv(cfg.HTTPSProxy, "HTTPS_PROXY", "https_proxy")), 77 NoProxy: getConfigOrEnv(cfg.NoProxy, "NO_PROXY", "no_proxy"), 78 LiveRestoreEnabled: cfg.LiveRestoreEnabled, 79 Isolation: daemon.defaultIsolation, 80 CDISpecDirs: promoteNil(cfg.CDISpecDirs), 81 } 82 83 daemon.fillContainerStates(v) 84 daemon.fillDebugInfo(ctx, v) 85 daemon.fillAPIInfo(v, &cfg.Config) 86 // Retrieve platform specific info 87 if err := daemon.fillPlatformInfo(ctx, v, sysInfo, cfg); err != nil { 88 return nil, err 89 } 90 daemon.fillDriverInfo(v) 91 daemon.fillPluginsInfo(ctx, v, &cfg.Config) 92 daemon.fillSecurityOptions(v, sysInfo, &cfg.Config) 93 daemon.fillLicense(v) 94 daemon.fillDefaultAddressPools(ctx, v, &cfg.Config) 95 96 return v, nil 97 } 98 99 // SystemVersion returns version information about the daemon. 100 // 101 // The only error this should return is due to context cancellation/deadline. 102 // Anything else should be logged and ignored because this is looking up 103 // multiple things and is often used for debugging. 104 // The only case valid early return is when the caller doesn't want the result anymore (ie context cancelled). 105 func (daemon *Daemon) SystemVersion(ctx context.Context) (types.Version, error) { 106 defer metrics.StartTimer(hostInfoFunctions.WithValues("system_version"))() 107 108 kernelVersion := kernelVersion(ctx) 109 cfg := daemon.config() 110 111 v := types.Version{ 112 Components: []types.ComponentVersion{ 113 { 114 Name: "Engine", 115 Version: dockerversion.Version, 116 Details: map[string]string{ 117 "GitCommit": dockerversion.GitCommit, 118 "ApiVersion": api.DefaultVersion, 119 "MinAPIVersion": cfg.MinAPIVersion, 120 "GoVersion": runtime.Version(), 121 "Os": runtime.GOOS, 122 "Arch": runtime.GOARCH, 123 "BuildTime": dockerversion.BuildTime, 124 "KernelVersion": kernelVersion, 125 "Experimental": fmt.Sprintf("%t", cfg.Experimental), 126 }, 127 }, 128 }, 129 130 // Populate deprecated fields for older clients 131 Version: dockerversion.Version, 132 GitCommit: dockerversion.GitCommit, 133 APIVersion: api.DefaultVersion, 134 MinAPIVersion: cfg.MinAPIVersion, 135 GoVersion: runtime.Version(), 136 Os: runtime.GOOS, 137 Arch: runtime.GOARCH, 138 BuildTime: dockerversion.BuildTime, 139 KernelVersion: kernelVersion, 140 Experimental: cfg.Experimental, 141 } 142 143 v.Platform.Name = dockerversion.PlatformName 144 145 if err := daemon.fillPlatformVersion(ctx, &v, cfg); err != nil { 146 return v, err 147 } 148 return v, nil 149 } 150 151 func (daemon *Daemon) fillDriverInfo(v *system.Info) { 152 v.Driver = daemon.imageService.StorageDriver() 153 v.DriverStatus = daemon.imageService.LayerStoreStatus() 154 155 const warnMsg = ` 156 WARNING: The %s storage-driver is deprecated, and will be removed in a future release. 157 Refer to the documentation for more information: https://docs.docker.com/go/storage-driver/` 158 159 switch v.Driver { 160 case "overlay": 161 v.Warnings = append(v.Warnings, fmt.Sprintf(warnMsg, v.Driver)) 162 } 163 164 fillDriverWarnings(v) 165 } 166 167 func (daemon *Daemon) fillPluginsInfo(ctx context.Context, v *system.Info, cfg *config.Config) { 168 v.Plugins = system.PluginsInfo{ 169 Volume: daemon.volumes.GetDriverList(), 170 Network: daemon.GetNetworkDriverList(ctx), 171 172 // The authorization plugins are returned in the order they are 173 // used as they constitute a request/response modification chain. 174 Authorization: cfg.AuthorizationPlugins, 175 Log: logger.ListDrivers(), 176 } 177 } 178 179 func (daemon *Daemon) fillSecurityOptions(v *system.Info, sysInfo *sysinfo.SysInfo, cfg *config.Config) { 180 var securityOptions []string 181 if sysInfo.AppArmor { 182 securityOptions = append(securityOptions, "name=apparmor") 183 } 184 if sysInfo.Seccomp && supportsSeccomp { 185 if daemon.seccompProfilePath != config.SeccompProfileDefault { 186 v.Warnings = append(v.Warnings, "WARNING: daemon is not using the default seccomp profile") 187 } 188 securityOptions = append(securityOptions, "name=seccomp,profile="+daemon.seccompProfilePath) 189 } 190 if selinux.GetEnabled() { 191 securityOptions = append(securityOptions, "name=selinux") 192 } 193 if rootIDs := daemon.idMapping.RootPair(); rootIDs.UID != 0 || rootIDs.GID != 0 { 194 securityOptions = append(securityOptions, "name=userns") 195 } 196 if Rootless(cfg) { 197 securityOptions = append(securityOptions, "name=rootless") 198 } 199 if cgroupNamespacesEnabled(sysInfo, cfg) { 200 securityOptions = append(securityOptions, "name=cgroupns") 201 } 202 if noNewPrivileges(cfg) { 203 securityOptions = append(securityOptions, "name=no-new-privileges") 204 } 205 206 v.SecurityOptions = securityOptions 207 } 208 209 func (daemon *Daemon) fillContainerStates(v *system.Info) { 210 cRunning, cPaused, cStopped := stateCtr.get() 211 v.Containers = cRunning + cPaused + cStopped 212 v.ContainersPaused = cPaused 213 v.ContainersRunning = cRunning 214 v.ContainersStopped = cStopped 215 } 216 217 // fillDebugInfo sets the current debugging state of the daemon, and additional 218 // debugging information, such as the number of Go-routines, and file descriptors. 219 // 220 // Note that this currently always collects the information, but the CLI only 221 // prints it if the daemon has debug enabled. We should consider to either make 222 // this information optional (cli to request "with debugging information"), or 223 // only collect it if the daemon has debug enabled. For the CLI code, see 224 // https://github.com/docker/cli/blob/v20.10.12/cli/command/system/info.go#L239-L244 225 func (daemon *Daemon) fillDebugInfo(ctx context.Context, v *system.Info) { 226 v.Debug = debug.IsEnabled() 227 v.NFd = fileutils.GetTotalUsedFds(ctx) 228 v.NGoroutines = runtime.NumGoroutine() 229 v.NEventsListener = daemon.EventsService.SubscribersCount() 230 } 231 232 func (daemon *Daemon) fillAPIInfo(v *system.Info, cfg *config.Config) { 233 const warn string = ` 234 Access to the remote API is equivalent to root access on the host. Refer 235 to the 'Docker daemon attack surface' section in the documentation for 236 more information: https://docs.docker.com/go/attack-surface/` 237 238 for _, host := range cfg.Hosts { 239 // cnf.Hosts is normalized during startup, so should always have a scheme/proto 240 proto, addr, _ := strings.Cut(host, "://") 241 if proto != "tcp" { 242 continue 243 } 244 if cfg.TLS == nil || !*cfg.TLS { 245 v.Warnings = append(v.Warnings, fmt.Sprintf("WARNING: API is accessible on http://%s without encryption.%s", addr, warn)) 246 continue 247 } 248 if cfg.TLSVerify == nil || !*cfg.TLSVerify { 249 v.Warnings = append(v.Warnings, fmt.Sprintf("WARNING: API is accessible on https://%s without TLS client verification.%s", addr, warn)) 250 continue 251 } 252 } 253 } 254 255 func (daemon *Daemon) fillDefaultAddressPools(ctx context.Context, v *system.Info, cfg *config.Config) { 256 _, span := tracing.StartSpan(ctx, "fillDefaultAddressPools") 257 defer span.End() 258 for _, pool := range cfg.DefaultAddressPools.Value() { 259 v.DefaultAddressPools = append(v.DefaultAddressPools, system.NetworkAddressPool{ 260 Base: pool.Base, 261 Size: pool.Size, 262 }) 263 } 264 } 265 266 func hostName(ctx context.Context) string { 267 ctx, span := tracing.StartSpan(ctx, "hostName") 268 defer span.End() 269 hostname := "" 270 if hn, err := os.Hostname(); err != nil { 271 log.G(ctx).Warnf("Could not get hostname: %v", err) 272 } else { 273 hostname = hn 274 } 275 return hostname 276 } 277 278 func kernelVersion(ctx context.Context) string { 279 ctx, span := tracing.StartSpan(ctx, "kernelVersion") 280 defer span.End() 281 282 var kernelVersion string 283 if kv, err := kernel.GetKernelVersion(); err != nil { 284 log.G(ctx).Warnf("Could not get kernel version: %v", err) 285 } else { 286 kernelVersion = kv.String() 287 } 288 return kernelVersion 289 } 290 291 func memInfo(ctx context.Context) *meminfo.Memory { 292 ctx, span := tracing.StartSpan(ctx, "memInfo") 293 defer span.End() 294 295 memInfo, err := meminfo.Read() 296 if err != nil { 297 log.G(ctx).Errorf("Could not read system memory info: %v", err) 298 memInfo = &meminfo.Memory{} 299 } 300 return memInfo 301 } 302 303 func operatingSystem(ctx context.Context) (operatingSystem string) { 304 ctx, span := tracing.StartSpan(ctx, "operatingSystem") 305 defer span.End() 306 307 defer metrics.StartTimer(hostInfoFunctions.WithValues("operating_system"))() 308 309 if s, err := operatingsystem.GetOperatingSystem(); err != nil { 310 log.G(ctx).Warnf("Could not get operating system name: %v", err) 311 } else { 312 operatingSystem = s 313 } 314 if inContainer, err := operatingsystem.IsContainerized(); err != nil { 315 log.G(ctx).Errorf("Could not determine if daemon is containerized: %v", err) 316 operatingSystem += " (error determining if containerized)" 317 } else if inContainer { 318 operatingSystem += " (containerized)" 319 } 320 321 return operatingSystem 322 } 323 324 func osVersion(ctx context.Context) (version string) { 325 ctx, span := tracing.StartSpan(ctx, "osVersion") 326 defer span.End() 327 328 defer metrics.StartTimer(hostInfoFunctions.WithValues("os_version"))() 329 330 version, err := operatingsystem.GetOperatingSystemVersion() 331 if err != nil { 332 log.G(ctx).Warnf("Could not get operating system version: %v", err) 333 } 334 335 return version 336 } 337 338 func getEnvAny(names ...string) string { 339 for _, n := range names { 340 if val := os.Getenv(n); val != "" { 341 return val 342 } 343 } 344 return "" 345 } 346 347 func getConfigOrEnv(config string, env ...string) string { 348 if config != "" { 349 return config 350 } 351 return getEnvAny(env...) 352 } 353 354 // promoteNil converts a nil slice to an empty slice. 355 // A non-nil slice is returned as is. 356 // 357 // TODO: make generic again once we are a go module, 358 // go.dev/issue/64759 is fixed, or we drop support for Go 1.21. 359 func promoteNil(s []string) []string { 360 if s == nil { 361 return []string{} 362 } 363 return s 364 }