github.com/sld880311/docker@v0.0.0-20200524143708-d5593973a475/cli/command/system/info.go (about) 1 package system 2 3 import ( 4 "fmt" 5 "sort" 6 "strings" 7 "time" 8 9 "github.com/docker/docker/api/types" 10 "github.com/docker/docker/api/types/swarm" 11 "github.com/docker/docker/cli" 12 "github.com/docker/docker/cli/command" 13 "github.com/docker/docker/pkg/ioutils" 14 "github.com/docker/docker/utils" 15 "github.com/docker/docker/utils/templates" 16 "github.com/docker/go-units" 17 "github.com/spf13/cobra" 18 "golang.org/x/net/context" 19 ) 20 21 type infoOptions struct { 22 format string 23 } 24 25 // NewInfoCommand creates a new cobra.Command for `docker info` 26 func NewInfoCommand(dockerCli *command.DockerCli) *cobra.Command { 27 var opts infoOptions 28 29 cmd := &cobra.Command{ 30 Use: "info [OPTIONS]", 31 Short: "Display system-wide information", 32 Args: cli.NoArgs, 33 RunE: func(cmd *cobra.Command, args []string) error { 34 return runInfo(dockerCli, &opts) 35 }, 36 } 37 38 flags := cmd.Flags() 39 40 flags.StringVarP(&opts.format, "format", "f", "", "Format the output using the given Go template") 41 42 return cmd 43 } 44 45 func runInfo(dockerCli *command.DockerCli, opts *infoOptions) error { 46 ctx := context.Background() 47 info, err := dockerCli.Client().Info(ctx) 48 if err != nil { 49 return err 50 } 51 if opts.format == "" { 52 return prettyPrintInfo(dockerCli, info) 53 } 54 return formatInfo(dockerCli, info, opts.format) 55 } 56 57 func prettyPrintInfo(dockerCli *command.DockerCli, info types.Info) error { 58 fmt.Fprintf(dockerCli.Out(), "Containers: %d\n", info.Containers) 59 fmt.Fprintf(dockerCli.Out(), " Running: %d\n", info.ContainersRunning) 60 fmt.Fprintf(dockerCli.Out(), " Paused: %d\n", info.ContainersPaused) 61 fmt.Fprintf(dockerCli.Out(), " Stopped: %d\n", info.ContainersStopped) 62 fmt.Fprintf(dockerCli.Out(), "Images: %d\n", info.Images) 63 ioutils.FprintfIfNotEmpty(dockerCli.Out(), "Server Version: %s\n", info.ServerVersion) 64 ioutils.FprintfIfNotEmpty(dockerCli.Out(), "Storage Driver: %s\n", info.Driver) 65 if info.DriverStatus != nil { 66 for _, pair := range info.DriverStatus { 67 fmt.Fprintf(dockerCli.Out(), " %s: %s\n", pair[0], pair[1]) 68 } 69 70 } 71 if info.SystemStatus != nil { 72 for _, pair := range info.SystemStatus { 73 fmt.Fprintf(dockerCli.Out(), "%s: %s\n", pair[0], pair[1]) 74 } 75 } 76 ioutils.FprintfIfNotEmpty(dockerCli.Out(), "Logging Driver: %s\n", info.LoggingDriver) 77 ioutils.FprintfIfNotEmpty(dockerCli.Out(), "Cgroup Driver: %s\n", info.CgroupDriver) 78 79 fmt.Fprintf(dockerCli.Out(), "Plugins: \n") 80 fmt.Fprintf(dockerCli.Out(), " Volume:") 81 fmt.Fprintf(dockerCli.Out(), " %s", strings.Join(info.Plugins.Volume, " ")) 82 fmt.Fprintf(dockerCli.Out(), "\n") 83 fmt.Fprintf(dockerCli.Out(), " Network:") 84 fmt.Fprintf(dockerCli.Out(), " %s", strings.Join(info.Plugins.Network, " ")) 85 fmt.Fprintf(dockerCli.Out(), "\n") 86 87 if len(info.Plugins.Authorization) != 0 { 88 fmt.Fprintf(dockerCli.Out(), " Authorization:") 89 fmt.Fprintf(dockerCli.Out(), " %s", strings.Join(info.Plugins.Authorization, " ")) 90 fmt.Fprintf(dockerCli.Out(), "\n") 91 } 92 93 fmt.Fprintf(dockerCli.Out(), "Swarm: %v\n", info.Swarm.LocalNodeState) 94 if info.Swarm.LocalNodeState != swarm.LocalNodeStateInactive && info.Swarm.LocalNodeState != swarm.LocalNodeStateLocked { 95 fmt.Fprintf(dockerCli.Out(), " NodeID: %s\n", info.Swarm.NodeID) 96 if info.Swarm.Error != "" { 97 fmt.Fprintf(dockerCli.Out(), " Error: %v\n", info.Swarm.Error) 98 } 99 fmt.Fprintf(dockerCli.Out(), " Is Manager: %v\n", info.Swarm.ControlAvailable) 100 if info.Swarm.ControlAvailable { 101 fmt.Fprintf(dockerCli.Out(), " ClusterID: %s\n", info.Swarm.Cluster.ID) 102 fmt.Fprintf(dockerCli.Out(), " Managers: %d\n", info.Swarm.Managers) 103 fmt.Fprintf(dockerCli.Out(), " Nodes: %d\n", info.Swarm.Nodes) 104 fmt.Fprintf(dockerCli.Out(), " Orchestration:\n") 105 taskHistoryRetentionLimit := int64(0) 106 if info.Swarm.Cluster.Spec.Orchestration.TaskHistoryRetentionLimit != nil { 107 taskHistoryRetentionLimit = *info.Swarm.Cluster.Spec.Orchestration.TaskHistoryRetentionLimit 108 } 109 fmt.Fprintf(dockerCli.Out(), " Task History Retention Limit: %d\n", taskHistoryRetentionLimit) 110 fmt.Fprintf(dockerCli.Out(), " Raft:\n") 111 fmt.Fprintf(dockerCli.Out(), " Snapshot Interval: %d\n", info.Swarm.Cluster.Spec.Raft.SnapshotInterval) 112 if info.Swarm.Cluster.Spec.Raft.KeepOldSnapshots != nil { 113 fmt.Fprintf(dockerCli.Out(), " Number of Old Snapshots to Retain: %d\n", *info.Swarm.Cluster.Spec.Raft.KeepOldSnapshots) 114 } 115 fmt.Fprintf(dockerCli.Out(), " Heartbeat Tick: %d\n", info.Swarm.Cluster.Spec.Raft.HeartbeatTick) 116 fmt.Fprintf(dockerCli.Out(), " Election Tick: %d\n", info.Swarm.Cluster.Spec.Raft.ElectionTick) 117 fmt.Fprintf(dockerCli.Out(), " Dispatcher:\n") 118 fmt.Fprintf(dockerCli.Out(), " Heartbeat Period: %s\n", units.HumanDuration(time.Duration(info.Swarm.Cluster.Spec.Dispatcher.HeartbeatPeriod))) 119 fmt.Fprintf(dockerCli.Out(), " CA Configuration:\n") 120 fmt.Fprintf(dockerCli.Out(), " Expiry Duration: %s\n", units.HumanDuration(info.Swarm.Cluster.Spec.CAConfig.NodeCertExpiry)) 121 if len(info.Swarm.Cluster.Spec.CAConfig.ExternalCAs) > 0 { 122 fmt.Fprintf(dockerCli.Out(), " External CAs:\n") 123 for _, entry := range info.Swarm.Cluster.Spec.CAConfig.ExternalCAs { 124 fmt.Fprintf(dockerCli.Out(), " %s: %s\n", entry.Protocol, entry.URL) 125 } 126 } 127 } 128 fmt.Fprintf(dockerCli.Out(), " Node Address: %s\n", info.Swarm.NodeAddr) 129 managers := []string{} 130 for _, entry := range info.Swarm.RemoteManagers { 131 managers = append(managers, entry.Addr) 132 } 133 if len(managers) > 0 { 134 sort.Strings(managers) 135 fmt.Fprintf(dockerCli.Out(), " Manager Addresses:\n") 136 for _, entry := range managers { 137 fmt.Fprintf(dockerCli.Out(), " %s\n", entry) 138 } 139 } 140 } 141 142 if len(info.Runtimes) > 0 { 143 fmt.Fprintf(dockerCli.Out(), "Runtimes:") 144 for name := range info.Runtimes { 145 fmt.Fprintf(dockerCli.Out(), " %s", name) 146 } 147 fmt.Fprint(dockerCli.Out(), "\n") 148 fmt.Fprintf(dockerCli.Out(), "Default Runtime: %s\n", info.DefaultRuntime) 149 } 150 151 if info.OSType == "linux" { 152 fmt.Fprintf(dockerCli.Out(), "Init Binary: %v\n", info.InitBinary) 153 154 for _, ci := range []struct { 155 Name string 156 Commit types.Commit 157 }{ 158 {"containerd", info.ContainerdCommit}, 159 {"runc", info.RuncCommit}, 160 {"init", info.InitCommit}, 161 } { 162 fmt.Fprintf(dockerCli.Out(), "%s version: %s", ci.Name, ci.Commit.ID) 163 if ci.Commit.ID != ci.Commit.Expected { 164 fmt.Fprintf(dockerCli.Out(), " (expected: %s)", ci.Commit.Expected) 165 } 166 fmt.Fprintf(dockerCli.Out(), "\n") 167 } 168 if len(info.SecurityOptions) != 0 { 169 kvs, err := types.DecodeSecurityOptions(info.SecurityOptions) 170 if err != nil { 171 return err 172 } 173 fmt.Fprintf(dockerCli.Out(), "Security Options:\n") 174 for _, so := range kvs { 175 fmt.Fprintf(dockerCli.Out(), " %s\n", so.Name) 176 for _, o := range so.Options { 177 switch o.Key { 178 case "profile": 179 if o.Value != "default" { 180 fmt.Fprintf(dockerCli.Err(), " WARNING: You're not using the default seccomp profile\n") 181 } 182 fmt.Fprintf(dockerCli.Out(), " Profile: %s\n", o.Value) 183 } 184 } 185 } 186 } 187 } 188 189 // Isolation only has meaning on a Windows daemon. 190 if info.OSType == "windows" { 191 fmt.Fprintf(dockerCli.Out(), "Default Isolation: %v\n", info.Isolation) 192 } 193 194 ioutils.FprintfIfNotEmpty(dockerCli.Out(), "Kernel Version: %s\n", info.KernelVersion) 195 ioutils.FprintfIfNotEmpty(dockerCli.Out(), "Operating System: %s\n", info.OperatingSystem) 196 ioutils.FprintfIfNotEmpty(dockerCli.Out(), "OSType: %s\n", info.OSType) 197 ioutils.FprintfIfNotEmpty(dockerCli.Out(), "Architecture: %s\n", info.Architecture) 198 fmt.Fprintf(dockerCli.Out(), "CPUs: %d\n", info.NCPU) 199 fmt.Fprintf(dockerCli.Out(), "Total Memory: %s\n", units.BytesSize(float64(info.MemTotal))) 200 ioutils.FprintfIfNotEmpty(dockerCli.Out(), "Name: %s\n", info.Name) 201 ioutils.FprintfIfNotEmpty(dockerCli.Out(), "ID: %s\n", info.ID) 202 fmt.Fprintf(dockerCli.Out(), "Docker Root Dir: %s\n", info.DockerRootDir) 203 fmt.Fprintf(dockerCli.Out(), "Debug Mode (client): %v\n", utils.IsDebugEnabled()) 204 fmt.Fprintf(dockerCli.Out(), "Debug Mode (server): %v\n", info.Debug) 205 206 if info.Debug { 207 fmt.Fprintf(dockerCli.Out(), " File Descriptors: %d\n", info.NFd) 208 fmt.Fprintf(dockerCli.Out(), " Goroutines: %d\n", info.NGoroutines) 209 fmt.Fprintf(dockerCli.Out(), " System Time: %s\n", info.SystemTime) 210 fmt.Fprintf(dockerCli.Out(), " EventsListeners: %d\n", info.NEventsListener) 211 } 212 213 ioutils.FprintfIfNotEmpty(dockerCli.Out(), "Http Proxy: %s\n", info.HTTPProxy) 214 ioutils.FprintfIfNotEmpty(dockerCli.Out(), "Https Proxy: %s\n", info.HTTPSProxy) 215 ioutils.FprintfIfNotEmpty(dockerCli.Out(), "No Proxy: %s\n", info.NoProxy) 216 217 if info.IndexServerAddress != "" { 218 u := dockerCli.ConfigFile().AuthConfigs[info.IndexServerAddress].Username 219 if len(u) > 0 { 220 fmt.Fprintf(dockerCli.Out(), "Username: %v\n", u) 221 } 222 fmt.Fprintf(dockerCli.Out(), "Registry: %v\n", info.IndexServerAddress) 223 } 224 225 if info.Labels != nil { 226 fmt.Fprintln(dockerCli.Out(), "Labels:") 227 for _, attribute := range info.Labels { 228 fmt.Fprintf(dockerCli.Out(), " %s\n", attribute) 229 } 230 // TODO: Engine labels with duplicate keys has been deprecated in 1.13 and will be error out 231 // after 3 release cycles (17.12). For now, a WARNING will be generated. The following will 232 // be removed eventually. 233 labelMap := map[string]string{} 234 for _, label := range info.Labels { 235 stringSlice := strings.SplitN(label, "=", 2) 236 if len(stringSlice) > 1 { 237 // If there is a conflict we will throw out an warning 238 if v, ok := labelMap[stringSlice[0]]; ok && v != stringSlice[1] { 239 fmt.Fprintln(dockerCli.Err(), "WARNING: labels with duplicate keys and conflicting values have been deprecated") 240 break 241 } 242 labelMap[stringSlice[0]] = stringSlice[1] 243 } 244 } 245 } 246 247 fmt.Fprintf(dockerCli.Out(), "Experimental: %v\n", info.ExperimentalBuild) 248 if info.ClusterStore != "" { 249 fmt.Fprintf(dockerCli.Out(), "Cluster Store: %s\n", info.ClusterStore) 250 } 251 252 if info.ClusterAdvertise != "" { 253 fmt.Fprintf(dockerCli.Out(), "Cluster Advertise: %s\n", info.ClusterAdvertise) 254 } 255 256 if info.RegistryConfig != nil && (len(info.RegistryConfig.InsecureRegistryCIDRs) > 0 || len(info.RegistryConfig.IndexConfigs) > 0) { 257 fmt.Fprintln(dockerCli.Out(), "Insecure Registries:") 258 for _, registry := range info.RegistryConfig.IndexConfigs { 259 if registry.Secure == false { 260 fmt.Fprintf(dockerCli.Out(), " %s\n", registry.Name) 261 } 262 } 263 264 for _, registry := range info.RegistryConfig.InsecureRegistryCIDRs { 265 mask, _ := registry.Mask.Size() 266 fmt.Fprintf(dockerCli.Out(), " %s/%d\n", registry.IP.String(), mask) 267 } 268 } 269 270 if info.RegistryConfig != nil && len(info.RegistryConfig.Mirrors) > 0 { 271 fmt.Fprintln(dockerCli.Out(), "Registry Mirrors:") 272 for _, mirror := range info.RegistryConfig.Mirrors { 273 fmt.Fprintf(dockerCli.Out(), " %s\n", mirror) 274 } 275 } 276 277 fmt.Fprintf(dockerCli.Out(), "Live Restore Enabled: %v\n\n", info.LiveRestoreEnabled) 278 279 // Only output these warnings if the server does not support these features 280 if info.OSType != "windows" { 281 printStorageDriverWarnings(dockerCli, info) 282 283 if !info.MemoryLimit { 284 fmt.Fprintln(dockerCli.Err(), "WARNING: No memory limit support") 285 } 286 if !info.SwapLimit { 287 fmt.Fprintln(dockerCli.Err(), "WARNING: No swap limit support") 288 } 289 if !info.KernelMemory { 290 fmt.Fprintln(dockerCli.Err(), "WARNING: No kernel memory limit support") 291 } 292 if !info.OomKillDisable { 293 fmt.Fprintln(dockerCli.Err(), "WARNING: No oom kill disable support") 294 } 295 if !info.CPUCfsQuota { 296 fmt.Fprintln(dockerCli.Err(), "WARNING: No cpu cfs quota support") 297 } 298 if !info.CPUCfsPeriod { 299 fmt.Fprintln(dockerCli.Err(), "WARNING: No cpu cfs period support") 300 } 301 if !info.CPUShares { 302 fmt.Fprintln(dockerCli.Err(), "WARNING: No cpu shares support") 303 } 304 if !info.CPUSet { 305 fmt.Fprintln(dockerCli.Err(), "WARNING: No cpuset support") 306 } 307 if !info.IPv4Forwarding { 308 fmt.Fprintln(dockerCli.Err(), "WARNING: IPv4 forwarding is disabled") 309 } 310 if !info.BridgeNfIptables { 311 fmt.Fprintln(dockerCli.Err(), "WARNING: bridge-nf-call-iptables is disabled") 312 } 313 if !info.BridgeNfIP6tables { 314 fmt.Fprintln(dockerCli.Err(), "WARNING: bridge-nf-call-ip6tables is disabled") 315 } 316 } 317 318 return nil 319 } 320 321 func printStorageDriverWarnings(dockerCli *command.DockerCli, info types.Info) { 322 if info.DriverStatus == nil { 323 return 324 } 325 326 for _, pair := range info.DriverStatus { 327 if pair[0] == "Data loop file" { 328 fmt.Fprintf(dockerCli.Err(), "WARNING: %s: usage of loopback devices is strongly discouraged for production use.\n Use `--storage-opt dm.thinpooldev` to specify a custom block storage device.\n", info.Driver) 329 } 330 if pair[0] == "Supports d_type" && pair[1] == "false" { 331 backingFs := getBackingFs(info) 332 333 msg := fmt.Sprintf("WARNING: %s: the backing %s filesystem is formatted without d_type support, which leads to incorrect behavior.\n", info.Driver, backingFs) 334 if backingFs == "xfs" { 335 msg += " Reformat the filesystem with ftype=1 to enable d_type support.\n" 336 } 337 msg += " Running without d_type support will not be supported in future releases." 338 fmt.Fprintln(dockerCli.Err(), msg) 339 } 340 } 341 } 342 343 func getBackingFs(info types.Info) string { 344 if info.DriverStatus == nil { 345 return "" 346 } 347 348 for _, pair := range info.DriverStatus { 349 if pair[0] == "Backing Filesystem" { 350 return pair[1] 351 } 352 } 353 return "" 354 } 355 356 func formatInfo(dockerCli *command.DockerCli, info types.Info, format string) error { 357 tmpl, err := templates.Parse(format) 358 if err != nil { 359 return cli.StatusError{StatusCode: 64, 360 Status: "Template parsing error: " + err.Error()} 361 } 362 err = tmpl.Execute(dockerCli.Out(), info) 363 dockerCli.Out().Write([]byte{'\n'}) 364 return err 365 }